From a45e0d198bf9d3355f0f5aaed14a66240834809a Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:30:34 -0400 Subject: [PATCH 1/2] Implement RFC#1117: Deprecate classic classes --- .../container/tests/container_test.js | 5 +- .../@ember/-internals/deprecations/index.ts | 7 ++ .../-internals/glimmer/lib/component.ts | 6 +- .../application/hot-reload-test.js | 3 +- .../components/attrs-lookup-test.js | 3 +- .../components/component-template-test.js | 5 +- .../components/curly-components-test.js | 13 +-- .../components/target-action-test.js | 7 +- .../integration/components/tracked-test.js | 3 +- .../tests/integration/helpers/tracked-test.js | 3 +- .../tests/integration/syntax/each-test.js | 3 +- .../-internals/metal/tests/computed_test.js | 3 +- .../metal/tests/native_desc_decorator_test.js | 13 +-- .../-internals/runtime/tests/helpers/array.js | 5 +- .../system/object/concatenated_test.js | 15 ++-- .../runtime/tests/mixins/array_test.js | 13 +-- .../runtime/tests/mixins/comparable_test.js | 3 +- .../tests/mixins/container_proxy_test.js | 3 +- .../tests/mixins/promise_proxy_test.js | 3 +- .../mixins/target_action_support_test.js | 23 ++--- .../tests/system/array_proxy/length_test.js | 3 +- .../runtime/tests/system/core_object_test.js | 3 +- .../tests/system/namespace/base_test.js | 7 +- .../runtime/tests/system/object_proxy_test.js | 5 +- .../-internals/views/lib/views/core_view.ts | 3 +- packages/@ember/application/index.ts | 3 +- packages/@ember/array/proxy.ts | 3 +- packages/@ember/controller/index.ts | 3 +- .../controller/tests/controller_test.js | 11 +-- packages/@ember/engine/index.ts | 5 +- packages/@ember/engine/instance.ts | 3 +- packages/@ember/object/core.ts | 40 ++++++--- packages/@ember/object/index.ts | 3 +- packages/@ember/object/lib/classic.ts | 67 ++++++++++++++ .../tests/classic-classes-deprecation-test.js | 90 +++++++++++++++++++ .../computed/dependent-key-compat-test.js | 5 +- .../computed/reduce_computed_macros_test.js | 23 +++-- packages/@ember/object/tests/computed_test.js | 23 ++--- packages/@ember/object/tests/create_test.js | 7 +- packages/@ember/object/tests/destroy_test.js | 13 +-- .../object/tests/es-compatibility-test.js | 9 +- packages/@ember/object/tests/evented_test.js | 3 +- packages/@ember/object/tests/events_test.js | 17 ++-- packages/@ember/object/tests/extend_test.js | 37 ++++---- .../@ember/object/tests/mixin/reopen_test.js | 3 +- .../@ember/object/tests/observable_test.js | 19 ++-- packages/@ember/object/tests/observer_test.js | 31 +++---- .../@ember/object/tests/reopenClass_test.js | 5 +- packages/@ember/object/tests/reopen_test.js | 7 +- .../@ember/routing/lib/routing-service.ts | 3 +- packages/@ember/routing/none-location.ts | 3 +- packages/@ember/routing/route.ts | 5 +- packages/@ember/routing/router.ts | 5 +- .../tests/location/history_location_test.js | 29 +++--- .../tests/location/none_location_test.js | 13 +-- .../@ember/routing/tests/system/dsl_test.js | 11 +-- packages/@ember/utils/tests/compare_test.js | 5 +- packages/@ember/utils/tests/type_of_test.js | 3 +- .../ember-testing/lib/adapters/adapter.ts | 3 +- .../tests/routing/decoupled_basic_test.js | 9 +- .../ember/tests/routing/query_params_test.js | 3 +- ..._dependent_state_with_query_params_test.js | 19 ++-- .../overlapping_query_params_test.js | 7 +- .../routing/router_service_test/basic_test.js | 3 +- .../router_service_test/recognize_test.js | 9 +- .../router_service_test/transitionTo_test.js | 23 ++--- .../ember/tests/service_injection_test.js | 3 +- .../lib/test-cases/application.ts | 3 +- .../lib/test-cases/autoboot-application.ts | 3 +- .../lib/test-cases/query-param.ts | 7 +- .../lib/test-cases/rendering.ts | 5 +- .../lib/test-cases/router-non-application.ts | 3 +- 72 files changed, 508 insertions(+), 259 deletions(-) create mode 100644 packages/@ember/object/lib/classic.ts create mode 100644 packages/@ember/object/tests/classic-classes-deprecation-test.js diff --git a/packages/@ember/-internals/container/tests/container_test.js b/packages/@ember/-internals/container/tests/container_test.js index 3417252bab5..b40b585987d 100644 --- a/packages/@ember/-internals/container/tests/container_test.js +++ b/packages/@ember/-internals/container/tests/container_test.js @@ -3,6 +3,7 @@ import Service from '@ember/service'; import { DEBUG } from '@glimmer/env'; import { Registry } from '..'; import { factory, moduleFor, AbstractTestCase, runTask } from 'internal-test-helpers'; +import { classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'Container.lookup', @@ -432,7 +433,7 @@ moduleFor( let Apple = factory(); let Orange = factory(); - Apple.reopenClass({ + classicReopenClass(Apple, { _lazyInjections() { return [{ specifier: 'orange:main' }, { specifier: 'banana:main' }]; }, @@ -459,7 +460,7 @@ moduleFor( let Apple = factory(); let Orange = factory(); - Apple.reopenClass({ + classicReopenClass(Apple, { _lazyInjections: () => { assert.ok(true, 'should call lazy injection method'); return [{ specifier: 'orange:main' }]; diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts index 0b0c42429ce..284d6ace4b9 100644 --- a/packages/@ember/-internals/deprecations/index.ts +++ b/packages/@ember/-internals/deprecations/index.ts @@ -133,6 +133,13 @@ export const DEPRECATIONS = { until: '8.0.0', url: 'https://deprecations.emberjs.com/id/deprecate-evented', }), + DEPRECATE_CLASSIC_CLASSES: deprecation({ + id: 'deprecate-classic-classes', + for: 'ember-source', + since: { available: '7.4.0' }, + until: '8.0.0', + url: 'https://deprecations.emberjs.com/id/deprecate-classic-classes', + }), }; export function deprecateUntil(message: string, deprecation: DeprecationObject) { diff --git a/packages/@ember/-internals/glimmer/lib/component.ts b/packages/@ember/-internals/glimmer/lib/component.ts index eb05265d007..321dc374cbe 100644 --- a/packages/@ember/-internals/glimmer/lib/component.ts +++ b/packages/@ember/-internals/glimmer/lib/component.ts @@ -32,6 +32,7 @@ import { getComponentCapturedArgs, } from './component-managers/curly'; import hasDOM from '@ember/-internals/browser-environment/lib/has-dom'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; // Keep track of which component classes have already been processed for lazy event setup. let lazyEventsProcessed = new WeakMap>(); @@ -802,7 +803,8 @@ interface Component extends CoreView, TargetActionSupport, ActionSupport, ComponentMethods {} class Component - extends CoreView.extend( + extends classicExtend( + CoreView, TargetActionSupport, ActionSupport, { @@ -1689,7 +1691,7 @@ class Component } // We continue to use reopenClass here so that positionalParams can be overridden with reopenClass in subclasses. -Component.reopenClass({ +classicReopenClass(Component, { positionalParams: [], }); diff --git a/packages/@ember/-internals/glimmer/tests/integration/application/hot-reload-test.js b/packages/@ember/-internals/glimmer/tests/integration/application/hot-reload-test.js index f5d4e92b16f..32a41e70766 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/application/hot-reload-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/application/hot-reload-test.js @@ -5,6 +5,7 @@ import { Component, Helper } from '@ember/-internals/glimmer'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; import templateOnly from '@ember/component/template-only'; +import { classicExtend } from '@ember/object/lib/classic'; function expect(value) { if (!value) { @@ -94,7 +95,7 @@ moduleFor( // with the template already set on the existing class let FreshClass; if (ComponentClass && 'extend' in ComponentClass) { - FreshClass = ComponentClass.extend({}); + FreshClass = classicExtend(ComponentClass, {}); } else { FreshClass = templateOnly(); } diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js index 631b532e336..b450e6e00d5 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/attrs-lookup-test.js @@ -5,6 +5,7 @@ import { setComponentTemplate } from '@glimmer/manager'; import { set, computed } from '@ember/object'; import { Component, htmlSafe } from '../../utils/helpers'; +import { classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'Components test: attrs lookup', @@ -171,7 +172,7 @@ moduleFor( } }; - FooBarComponent.reopenClass({ + classicReopenClass(FooBarComponent, { positionalParams: ['firstPositional'], }); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/component-template-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/component-template-test.js index 5ec0eee1e39..47cc0ef0f30 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/component-template-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/component-template-test.js @@ -4,6 +4,7 @@ import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; import { setComponentTemplate, getComponentTemplate } from '@glimmer/manager'; import { precompileTemplate } from '@ember/template-compilation'; import { Component } from '../../utils/helpers'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'Components test: setComponentTemplate', @@ -66,7 +67,7 @@ moduleFor( let Thing = setComponentTemplate( precompileTemplate('hello'), - Component.extend().reopenClass({ + classicReopenClass(classicExtend(Component), { toString() { return 'Thing'; }, @@ -78,7 +79,7 @@ moduleFor( }, /Cannot call `setComponentTemplate` multiple times on the same class \(`Class`\)/); } - '@test templates set with setComponentTemplate are inherited (EmberObject.extend())'() { + '@test templates set with setComponentTemplate are inherited (classicExtend(EmberObject, ))'() { let Parent = setComponentTemplate(precompileTemplate('hello'), class extends Component {}); this.owner.register('component:foo-bar', class extends Parent {}); 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 41420b751bf..c429bf23648 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 @@ -28,6 +28,7 @@ import { setComponentTemplate } from '@glimmer/manager'; import templateOnly from '@ember/component/template-only'; import { backtrackingMessageFor } from '../../utils/debug-stack'; import { precompileTemplate } from '@ember/template-compilation'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'Components test: curly components', @@ -1219,7 +1220,7 @@ moduleFor( // import Component from '@ember/component'; // import layout from './template'; // - // export default Component.extend({ + // export default classicExtend(Component, { // layout // }); let hello = precompileTemplate('Hello'); @@ -3376,7 +3377,7 @@ moduleFor( 'component:foo-bar', setComponentTemplate( precompileTemplate('{{this.bar}}-{{this.barCopy}}'), - Component.extend({ + classicExtend(Component, { init() { this._super(...arguments); this.didInit = true; @@ -3454,7 +3455,7 @@ moduleFor( () => { this.owner.register( 'component:evented-component', - Component.extend({ + classicExtend(Component, { someTruthyProperty: true, init() { @@ -3559,7 +3560,7 @@ moduleFor( template( 'MyVar1: {{attrs.myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{attrs.myVar2}}', { - component: MyComponent.reopenClass({ positionalParams: ['myVar'] }), + component: classicReopenClass(MyComponent, { positionalParams: ['myVar'] }), strictMode: false, } ) @@ -3577,7 +3578,7 @@ moduleFor( template( 'MyVar1: {{this.attrs.myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{this.attrs.myVar2}}', { - component: MyComponent.reopenClass({ positionalParams: ['myVar'] }), + component: classicReopenClass(MyComponent, { positionalParams: ['myVar'] }), strictMode: false, } ) @@ -3598,7 +3599,7 @@ moduleFor( precompileTemplate( 'MyVar1: {{@myVar}} {{this.myVar}} MyVar2: {{this.myVar2}} {{@myVar2}}' ), - MyComponent.reopenClass({ + classicReopenClass(MyComponent, { positionalParams: ['myVar'], }) ) diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js index 2b6c77a479d..00b6270d11b 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/target-action-test.js @@ -13,6 +13,7 @@ import EmberObject from '@ember/object'; import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { Component } from '../../utils/helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Components test: send', @@ -99,7 +100,7 @@ moduleFor( this.owner.register( 'component:foo-bar', - Component.extend({ + classicExtend(Component, { init() { this._super(...arguments); component = this; @@ -110,7 +111,7 @@ moduleFor( return true; }, }, - target: Controller.extend({ + target: classicExtend(Controller, { actions: { poke: () => { this.assert.ok(true, 'action bubbled to controller'); @@ -160,7 +161,7 @@ moduleFor( this.owner.register( 'component:x-index', - class extends SuperComponent.extend(BarViewMixin) { + class extends classicExtend(SuperComponent, BarViewMixin) { init() { super.init(...arguments); component = this; diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js index c1d91f6f668..a87876aed0c 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/tracked-test.js @@ -12,6 +12,7 @@ import GlimmerishComponent from '../../utils/glimmerish-component'; import { Component } from '../../utils/helpers'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Component Tracked Properties', @@ -96,7 +97,7 @@ moduleFor( } '@test creating an array proxy inside a tracking context does not trigger backtracking assertion'() { - let PromiseArray = ArrayProxy.extend(PromiseProxyMixin); + let PromiseArray = classicExtend(ArrayProxy, PromiseProxyMixin); class LoaderComponent extends GlimmerishComponent { get data() { diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js index dae53ef767d..01494288736 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js @@ -12,6 +12,7 @@ import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; import { Component } from '../../utils/helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Helper Tracked Properties', @@ -177,7 +178,7 @@ moduleFor( } '@test custom ember array properties rerender when updated'() { - let CustomArray = class extends EmberObject.extend(MutableArray) { + let CustomArray = class extends classicExtend(EmberObject, MutableArray) { init() { super.init(...arguments); this._vals = [1, 2, 3]; 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 ac02dd5df23..db3f02f64b7 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/each-test.js @@ -15,6 +15,7 @@ import { FalsyGenerator, ArrayTestCases, } from '../../utils/shared-conditional-tests'; +import { classicExtend } from '@ember/object/lib/classic'; class ArrayDelegate { constructor(content, target) { @@ -1114,7 +1115,7 @@ moduleFor( class extends EachTest { createList(items) { let wrapped = emberA(items); - let proxy = ArrayProxy.extend({ + let proxy = classicExtend(ArrayProxy, { init: function () { this._super(...arguments); diff --git a/packages/@ember/-internals/metal/tests/computed_test.js b/packages/@ember/-internals/metal/tests/computed_test.js index 1a20fc2166c..f413fe10fc3 100644 --- a/packages/@ember/-internals/metal/tests/computed_test.js +++ b/packages/@ember/-internals/metal/tests/computed_test.js @@ -12,6 +12,7 @@ import { import { run } from '@ember/runloop'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; import { destroy } from '@glimmer/destroyable'; +import { classicExtend } from '@ember/object/lib/classic'; let obj, objA, objB, count, func; @@ -731,7 +732,7 @@ moduleFor( } ['@test the return value of the setter gets cached'](assert) { - let testObj = EmberObject.extend({ + let testObj = classicExtend(EmberObject, { a: '1', sampleCP: computed('a', { get() { diff --git a/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js b/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js index 6d955965051..f058a4c80be 100644 --- a/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js +++ b/packages/@ember/-internals/metal/tests/native_desc_decorator_test.js @@ -2,6 +2,7 @@ import EmberObject from '@ember/object'; import { defineProperty, nativeDescDecorator } from '..'; import Mixin from '@ember/object/mixin'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; let classes = [ class { @@ -66,7 +67,7 @@ let classes = [ class { static module(title) { - return `${title}: in EmberObject.extend()`; + return `${title}: in classicExtend(EmberObject, )`; } constructor() { @@ -83,7 +84,7 @@ let classes = [ } finalize() { - this.klass = EmberObject.extend(this.props); + this.klass = classicExtend(EmberObject, this.props); return this.klass.create(); } @@ -94,7 +95,7 @@ let classes = [ class { static module(title) { - return `${title}: in EmberObject.extend() through a mixin`; + return `${title}: in classicExtend(EmberObject, ) through a mixin`; } constructor() { @@ -111,7 +112,7 @@ let classes = [ } finalize() { - this.klass = EmberObject.extend(Mixin.create(this.props)); + this.klass = classicExtend(EmberObject, Mixin.create(this.props)); return this.klass.create(); } @@ -139,8 +140,8 @@ let classes = [ } finalize() { - this.superklass = EmberObject.extend(this.props); - return this.superklass.extend().create(); + this.superklass = classicExtend(EmberObject, this.props); + return classicExtend(this.superklass).create(); } source() { diff --git a/packages/@ember/-internals/runtime/tests/helpers/array.js b/packages/@ember/-internals/runtime/tests/helpers/array.js index 68e4aa22fad..efcdcf8fd45 100644 --- a/packages/@ember/-internals/runtime/tests/helpers/array.js +++ b/packages/@ember/-internals/runtime/tests/helpers/array.js @@ -10,6 +10,7 @@ import { } from '@ember/-internals/metal'; import EmberObject, { get, computed } from '@ember/object'; import { moduleFor } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; export function newFixture(cnt) { let ret = []; @@ -168,7 +169,7 @@ class ArrayProxyHelpers extends AbstractArrayHelper { Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestArray = EmberObject.extend(EmberArray, { +const TestArray = classicExtend(EmberObject, EmberArray, { _content: null, init() { @@ -204,7 +205,7 @@ const TestArray = EmberObject.extend(EmberArray, { Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestMutableArray = EmberObject.extend(MutableArray, { +const TestMutableArray = classicExtend(EmberObject, MutableArray, { _content: null, init(ary = []) { diff --git a/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js b/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js index b20600f4be2..813b8ac8242 100644 --- a/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js +++ b/packages/@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test.js @@ -1,5 +1,6 @@ import EmberObject, { get } from '@ember/object'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend, classicReopen } from '@ember/object/lib/classic'; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests @@ -23,7 +24,7 @@ moduleFor( 'EmberObject Concatenated Properties', class extends AbstractTestCase { beforeEach() { - klass = EmberObject.extend({ + klass = classicExtend(EmberObject, { concatenatedProperties: ['values', 'functions'], values: ['a', 'b', 'c'], functions: [K], @@ -46,7 +47,7 @@ moduleFor( } ['@test concatenates subclasses'](assert) { - let subKlass = klass.extend({ + let subKlass = classicExtend(klass, { values: ['d', 'e', 'f'], }); let obj = subKlass.create(); @@ -62,7 +63,7 @@ moduleFor( } ['@test concatenates reopen'](assert) { - klass.reopen({ + classicReopen(klass, { values: ['d', 'e', 'f'], }); let obj = klass.create(); @@ -81,7 +82,7 @@ moduleFor( let mixin = { values: ['d', 'e'], }; - let subKlass = klass.extend(mixin, { + let subKlass = classicExtend(klass, mixin, { values: ['f'], }); let obj = subKlass.create(); @@ -97,8 +98,8 @@ moduleFor( } ['@test concatenates reopen, subclass, and instance'](assert) { - klass.reopen({ values: ['d'] }); - let subKlass = klass.extend({ values: ['e'] }); + classicReopen(klass, { values: ['d'] }); + let subKlass = classicExtend(klass, { values: ['e'] }); let obj = subKlass.create({ values: ['f'] }); let values = get(obj, 'values'); @@ -112,7 +113,7 @@ moduleFor( } ['@test concatenates subclasses when the values are functions'](assert) { - let subKlass = klass.extend({ + let subKlass = classicExtend(klass, { functions: K, }); let obj = subKlass.create(); diff --git a/packages/@ember/-internals/runtime/tests/mixins/array_test.js b/packages/@ember/-internals/runtime/tests/mixins/array_test.js index bf05aa13c7d..54f2e2557c6 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/array_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/array_test.js @@ -9,12 +9,13 @@ import { import EmberObject, { get, set, computed, observer as emberObserver } from '@ember/object'; import EmberArray, { A as emberA } from '@ember/array'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; /* Implement a basic fake mutable array. This validates that any non-native enumerable can impl this API. */ -const TestArray = class extends EmberObject.extend(EmberArray) { +const TestArray = class extends classicExtend(EmberObject, EmberArray) { _content = null; init() { @@ -50,7 +51,7 @@ moduleFor( 'Ember.Array', class extends AbstractTestCase { ['@test the return value of slice has Ember.Array applied'](assert) { - let x = EmberObject.extend(EmberArray).create({ + let x = classicExtend(EmberObject, EmberArray).create({ length: 0, }); let y = x.slice(1); @@ -80,7 +81,7 @@ moduleFor( // CONTENT DID CHANGE // -class DummyArray extends EmberObject.extend(EmberArray) { +class DummyArray extends classicExtend(EmberObject, EmberArray) { length = 0; objectAt(idx) { return 'ITEM-' + idx; @@ -97,7 +98,7 @@ moduleFor( 'mixins/array/arrayContent[Will|Did]Change', class extends AbstractTestCase { async ['@test should notify observers of []'](assert) { - obj = DummyArray.extend({ + obj = classicExtend(DummyArray, { enumerablePropertyDidChange: emberObserver('[]', function () { this._count++; }), @@ -129,7 +130,7 @@ moduleFor( 'notify observers of length', class extends AbstractTestCase { beforeEach(assert) { - obj = DummyArray.extend({ + obj = classicExtend(DummyArray, { lengthDidChange: emberObserver('length', function () { this._after++; }), @@ -366,7 +367,7 @@ moduleFor( ) { let count = 0; - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { init() { this._super(...arguments); // Observer does not fire on init diff --git a/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js b/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js index edf437836dd..2cd98502d0d 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/comparable_test.js @@ -3,8 +3,9 @@ import { compare } from '@ember/utils'; import Comparable from '../../lib/mixins/comparable'; import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../deprecations'; +import { classicExtend } from '@ember/object/lib/classic'; -class Rectangle extends EmberObject.extend(Comparable) { +class Rectangle extends classicExtend(EmberObject, Comparable) { length = 0; width = 0; diff --git a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js b/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js index 1f9cbcfa175..f4df79f33ad 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js @@ -5,12 +5,13 @@ import EmberObject from '@ember/object'; import { run, schedule } from '@ember/runloop'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; import { destroy } from '@glimmer/destroyable'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( '@ember/-internals/runtime/mixins/container_proxy', class extends AbstractTestCase { beforeEach() { - this.Owner = EmberObject.extend(ContainerProxy); + this.Owner = classicExtend(EmberObject, ContainerProxy); this.instance = this.Owner.create(); this.registry = new Registry(); diff --git a/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js b/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js index 99822d559fd..5b312b1e0d5 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/promise_proxy_test.js @@ -6,6 +6,7 @@ import EmberRSVP from '../../lib/ext/rsvp'; import { onerrorDefault } from '../../lib/ext/rsvp'; import * as RSVP from 'rsvp'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; let ObjectPromiseProxy, proxy; @@ -13,7 +14,7 @@ moduleFor( 'Ember.PromiseProxy - ObjectProxy', class extends AbstractTestCase { beforeEach() { - ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin); + ObjectPromiseProxy = classicExtend(ObjectProxy, PromiseProxyMixin); } afterEach() { diff --git a/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js b/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js index 13fae5111ed..f8d1b279733 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/target_action_support_test.js @@ -3,6 +3,7 @@ import EmberObject from '@ember/object'; import TargetActionSupport from '../../lib/mixins/target_action_support'; import { expectDeprecation, moduleFor, AbstractTestCase, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../../deprecations'; +import { classicExtend } from '@ember/object/lib/classic'; let originalLookup = context.lookup; let lookup; @@ -28,7 +29,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create(); + let obj = classicExtend(EmberObject, TargetActionSupport).create(); assert.ok(false === obj.triggerAction(), 'no target or action was specified'); } @@ -43,7 +44,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ anEvent() { assert.ok(true, 'anEvent method was called'); @@ -66,7 +67,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ send(evt, context) { assert.equal(evt, 'anEvent', 'send() method was invoked with correct event name'); @@ -99,7 +100,7 @@ moduleFor( }, }); - let myObj = EmberObject.extend(TargetActionSupport).create({ + let myObj = classicExtend(EmberObject, TargetActionSupport).create({ target: 'Test.targetObj', action: 'anEvent', }); @@ -117,7 +118,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ action: 'anEvent', actionContext: {}, target: EmberObject.create({ @@ -147,7 +148,7 @@ moduleFor( lookup.Test = Test; Test.aContext = {}; - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ action: 'anEvent', actionContext: 'Test.aContext', target: EmberObject.create({ @@ -175,7 +176,7 @@ moduleFor( assert.ok(true, 'anEvent method was called'); }, }); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ action: 'anEvent', }); @@ -195,7 +196,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ anEvent() { assert.ok(true, 'anEvent method was called'); @@ -220,7 +221,7 @@ moduleFor( ); let context = {}; - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ anEvent(ctx) { assert.ok(context === ctx, 'anEvent method was called with the expected context'); @@ -247,7 +248,7 @@ moduleFor( let param1 = 'someParam'; let param2 = 'someOtherParam'; - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ anEvent(first, second) { assert.ok( @@ -279,7 +280,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let obj = EmberObject.extend(TargetActionSupport).create({ + let obj = classicExtend(EmberObject, TargetActionSupport).create({ target: EmberObject.create({ anEvent(ctx) { assert.ok(null === ctx, 'anEvent method was called with the expected context (null)'); diff --git a/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js b/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js index 41ce4894f9e..d1d3246d6b6 100644 --- a/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js +++ b/packages/@ember/-internals/runtime/tests/system/array_proxy/length_test.js @@ -5,6 +5,7 @@ import { A as a } from '@ember/array'; import { moduleFor, AbstractTestCase, runTask, runLoopSettled } from 'internal-test-helpers'; import { set, get } from '@ember/object'; import { createCache, getValue } from '@glimmer/validator'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Ember.ArrayProxy - content change (length)', @@ -158,7 +159,7 @@ moduleFor( aCalled = bCalled = cCalled = dCalled = eCalled = 0; - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { colors: reads('model'), length: reads('colors.length'), diff --git a/packages/@ember/-internals/runtime/tests/system/core_object_test.js b/packages/@ember/-internals/runtime/tests/system/core_object_test.js index 1649c657a4d..c6a91cd503b 100644 --- a/packages/@ember/-internals/runtime/tests/system/core_object_test.js +++ b/packages/@ember/-internals/runtime/tests/system/core_object_test.js @@ -11,6 +11,7 @@ import { import { track } from '@glimmer/validator'; import { destroy } from '@glimmer/destroyable'; import { run } from '@ember/runloop'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Ember.CoreObject', @@ -99,7 +100,7 @@ moduleFor( async ['@test observed properties are enumerable when set GH#14594'](assert) { let callCount = 0; - let Test = CoreObject.extend({ + let Test = classicExtend(CoreObject, { myProp: null, anotherProp: undefined, didChangeMyProp: observer('myProp', function () { diff --git a/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js b/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js index a1f72771eba..6b0f34ca413 100644 --- a/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js +++ b/packages/@ember/-internals/runtime/tests/system/namespace/base_test.js @@ -5,6 +5,7 @@ import { guidFor, getName } from '@ember/-internals/utils'; import EmberObject from '@ember/object'; import Namespace from '@ember/application/namespace'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; const originalLookup = context.lookup; let lookup; @@ -61,16 +62,16 @@ moduleFor( ['@test Classes under an Namespace are properly named'](assert) { let nsA = (lookup.NamespaceA = Namespace.create()); - nsA.Foo = EmberObject.extend(); + nsA.Foo = classicExtend(EmberObject); Namespace.processAll(); assert.equal(getName(nsA.Foo), 'NamespaceA.Foo', 'Classes pick up their parent namespace'); - nsA.Bar = EmberObject.extend(); + nsA.Bar = classicExtend(EmberObject); Namespace.processAll(); assert.equal(getName(nsA.Bar), 'NamespaceA.Bar', 'New Classes get the naming treatment too'); let nsB = (lookup.NamespaceB = Namespace.create()); - nsB.Foo = EmberObject.extend(); + nsB.Foo = classicExtend(EmberObject); Namespace.processAll(); assert.equal( getName(nsB.Foo), diff --git a/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js b/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js index c5279308e64..3614cba38f9 100644 --- a/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js +++ b/packages/@ember/-internals/runtime/tests/system/object_proxy_test.js @@ -3,6 +3,7 @@ import { addObserver, removeObserver } from '@ember/-internals/metal'; import { computed, get, set, observer } from '@ember/object'; import ObjectProxy from '@ember/object/proxy'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'ObjectProxy', @@ -137,7 +138,7 @@ moduleFor( } [`@test setting a property on the proxy's prototype avoids the assertion`](assert) { - let proxy = ObjectProxy.extend({ + let proxy = classicExtend(ObjectProxy, { toJSON: null, }).create({ content: { @@ -337,7 +338,7 @@ moduleFor( ) { assert.expect(0); - let obj = ObjectProxy.extend({ + let obj = classicExtend(ObjectProxy, { observe: observer('foo', function () {}), }).create(); diff --git a/packages/@ember/-internals/views/lib/views/core_view.ts b/packages/@ember/-internals/views/lib/views/core_view.ts index 2f0e757da06..0449666dbfb 100644 --- a/packages/@ember/-internals/views/lib/views/core_view.ts +++ b/packages/@ember/-internals/views/lib/views/core_view.ts @@ -13,6 +13,7 @@ import Evented from '@ember/object/evented'; import { FrameworkObject } from '@ember/object/-internals'; import type { ViewState } from './states'; import states from './states'; +import { classicExtend } from '@ember/object/lib/classic'; /** `CoreView` is an abstract class that exists to give view-like behavior @@ -32,7 +33,7 @@ import states from './states'; */ interface CoreView extends ActionHandler, View {} -class CoreView extends FrameworkObject.extend(ActionHandler) { +class CoreView extends classicExtend(FrameworkObject, ActionHandler) { static { // The deprecated Evented mixin is no longer applied, but instances still // provide its methods, so `Evented.detect` must keep returning true. diff --git a/packages/@ember/application/index.ts b/packages/@ember/application/index.ts index e4ac76f8de3..d6ac88e3859 100644 --- a/packages/@ember/application/index.ts +++ b/packages/@ember/application/index.ts @@ -29,6 +29,7 @@ import { setupApplicationRegistry } from '@ember/-internals/glimmer/lib/setup-re import RouterService from '@ember/routing/router-service'; import type { EngineInstanceOptions } from '@ember/engine/instance'; import type { SimpleDocument, SimpleElement } from '@simple-dom/interface'; +import { classicExtend } from '@ember/object/lib/classic'; /** * @deprecated Use `import { getOwner } from '@ember/owner';` instead. @@ -386,7 +387,7 @@ class Application extends Engine { // Create subclass of Router for this Application instance. // This is to ensure that someone reopening `App.Router` does not // tamper with the default `Router`. - this.Router = (this.Router || Router).extend() as typeof Router; + this.Router = classicExtend(this.Router || Router) as typeof Router; this._buildDeprecatedInstance(); this.waitForDOMReady(); diff --git a/packages/@ember/array/proxy.ts b/packages/@ember/array/proxy.ts index d4a9907c2ff..d744e0975de 100644 --- a/packages/@ember/array/proxy.ts +++ b/packages/@ember/array/proxy.ts @@ -28,6 +28,7 @@ import { import { consumeTag } from '@glimmer/validator/lib/tracking'; import { tagFor } from '@glimmer/validator/lib/meta'; import type { Tag } from '@glimmer/interfaces'; +import { classicReopen } from '@ember/object/lib/classic'; function isMutable(obj: T[] | EmberArray): obj is T[] | MutableArray { return Array.isArray(obj) || typeof (obj as MutableArray).replace === 'function'; @@ -405,7 +406,7 @@ class ArrayProxy extends EmberObject implements PropertyDidChange { } } -ArrayProxy.reopen(MutableArray, { +classicReopen(ArrayProxy, MutableArray, { arrangedContent: alias('content'), }); diff --git a/packages/@ember/controller/index.ts b/packages/@ember/controller/index.ts index 0d86064d773..5f3329e3aab 100644 --- a/packages/@ember/controller/index.ts +++ b/packages/@ember/controller/index.ts @@ -11,6 +11,7 @@ import Mixin from '@ember/object/mixin'; import type { RouteArgs } from '@ember/routing/-internals'; import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler'; import type { Transition } from 'router_js'; +import { classicExtend } from '@ember/object/lib/classic'; export type ControllerQueryParamType = 'boolean' | 'number' | 'array' | 'string'; export type ControllerQueryParam = @@ -315,7 +316,7 @@ const ControllerMixin = Mixin.create(ActionHandler, { @public */ interface Controller<_T = unknown> extends FrameworkObject, ControllerMixin<_T> {} -class Controller<_T = unknown> extends FrameworkObject.extend(ControllerMixin) {} +class Controller<_T = unknown> extends classicExtend(FrameworkObject, ControllerMixin) {} /** Creates a property that lazily looks up another controller in the container. diff --git a/packages/@ember/controller/tests/controller_test.js b/packages/@ember/controller/tests/controller_test.js index 3b9dc8ef131..374aa23ae9b 100644 --- a/packages/@ember/controller/tests/controller_test.js +++ b/packages/@ember/controller/tests/controller_test.js @@ -16,6 +16,7 @@ import { import { DEPRECATIONS } from '@ember/-internals/deprecations'; import { action } from '@ember/object'; import { precompileTemplate } from '@ember/template-compilation'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Controller model', @@ -98,7 +99,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let TestController = Controller.extend({ + let TestController = classicExtend(Controller, { actions: { poke() { assert.ok(true, 'poked'); @@ -121,7 +122,7 @@ moduleFor( let owner = buildOwner(); - let TestController = Controller.extend({ + let TestController = classicExtend(Controller, { actions: { poke() { assert.ok(true, 'poked 1'); @@ -133,7 +134,7 @@ moduleFor( owner.register('controller:index', TestController); let controller = TestController.create({ - target: Controller.extend({ + target: classicExtend(Controller, { actions: { poke() { assert.ok(true, 'poked 2'); @@ -159,7 +160,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_TARGET_ACTION_SUPPORT.isEnabled ); - let SuperController = Controller.extend({ + let SuperController = classicExtend(Controller, { actions: { foo() { assert.ok(true, 'foo'); @@ -179,7 +180,7 @@ moduleFor( }, }); - let IndexController = SuperController.extend(BarControllerMixin, { + let IndexController = classicExtend(SuperController, BarControllerMixin, { actions: { baz() { assert.ok(true, 'baz'); diff --git a/packages/@ember/engine/index.ts b/packages/@ember/engine/index.ts index 478de9e9b13..88593c0dd3a 100644 --- a/packages/@ember/engine/index.ts +++ b/packages/@ember/engine/index.ts @@ -16,6 +16,7 @@ import { RoutingService } from '@ember/routing/-internals'; import { setupEngineRegistry } from '@ember/-internals/glimmer/lib/setup-registry'; import RegistryProxyMixin from '@ember/-internals/runtime/lib/mixins/registry_proxy'; import { StrictResolver } from './lib/strict-resolver'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; function props(obj: object) { let properties = []; @@ -56,7 +57,7 @@ export interface Initializer { */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface Engine extends RegistryProxyMixin {} -class Engine extends Namespace.extend(RegistryProxyMixin) { +class Engine extends classicExtend(Namespace, RegistryProxyMixin) { static initializers: Record> = Object.create(null); static instanceInitializers: Record> = Object.create(null); @@ -494,7 +495,7 @@ export function buildInitializerMethod< let attrs = { [bucketName]: Object.create(this[bucketName]), }; - this.reopenClass(attrs); + classicReopenClass(this, attrs); } assert( diff --git a/packages/@ember/engine/instance.ts b/packages/@ember/engine/instance.ts index 050d27eb3b8..7482e5903ae 100644 --- a/packages/@ember/engine/instance.ts +++ b/packages/@ember/engine/instance.ts @@ -17,6 +17,7 @@ import type Engine from '@ember/engine'; import type Application from '@ember/application'; import type { BootEnvironment } from '@ember/-internals/glimmer/lib/views/outlet'; import type { SimpleElement } from '@simple-dom/interface'; +import { classicExtend } from '@ember/object/lib/classic'; export interface BootOptions { isBrowser?: boolean; @@ -53,7 +54,7 @@ export interface EngineInstanceOptions { // type checking, we have broken part of our public API contract. Medium-term, // the goal here is to `EngineInstance` simple be `Owner`. interface EngineInstance extends RegistryProxyMixin, ContainerProxyMixin, InternalOwner, Owner {} -class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerProxyMixin) { +class EngineInstance extends classicExtend(EmberObject, RegistryProxyMixin, ContainerProxyMixin) { /** @private @method setupRegistry diff --git a/packages/@ember/object/core.ts b/packages/@ember/object/core.ts index 957bfb224f7..d548853a32b 100644 --- a/packages/@ember/object/core.ts +++ b/packages/@ember/object/core.ts @@ -14,10 +14,17 @@ import { activateObserver } from '@ember/-internals/metal/lib/observer'; import { defineProperty } from '@ember/-internals/metal/lib/properties'; import { descriptorForProperty, isClassicDecorator } from '@ember/-internals/metal/lib/decorator'; import { DEBUG_INJECTION_FUNCTIONS } from '@ember/-internals/metal/lib/injected_property'; -import Mixin, { applyMixin } from '@ember/object/mixin'; +import Mixin from '@ember/object/mixin'; import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler'; import makeArray from '@ember/array/make'; import { assert } from '@ember/debug'; +import { deprecateUntil, DEPRECATIONS } from '@ember/-internals/deprecations'; +import { + classicExtend, + classicReopen, + classicReopenClass, + classicReopenInstance, +} from './lib/classic'; import { DEBUG } from '@glimmer/env'; import { destroy, isDestroying, isDestroyed, registerDestructor } from '@glimmer/destroyable'; import { OWNER } from '@glimmer/owner'; @@ -51,7 +58,6 @@ function hasToStringExtension(val: unknown): val is HasToStringExtension { typeof (val as HasToStringExtension).toStringExtension === 'function' ); } -const reopen = Mixin.prototype.reopen; const wasApplied = new WeakSet(); const prototypeMixinMap = new WeakMap(); @@ -310,8 +316,11 @@ class CoreObject { } reopen(...args: Array>): this { - applyMixin(this, args); - return this; + deprecateUntil( + '`reopen` is part of the classic class system, which is deprecated. Define the properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return classicReopenInstance(this, ...args); } /** @@ -712,9 +721,11 @@ class CoreObject { ...mixins: M ): Readonly & EmberClassConstructor & MergeArray; static extend(...mixins: any[]) { - let Class = class extends this {}; - reopen.apply(Class.PrototypeMixin, mixins); - return Class; + deprecateUntil( + '`.extend()` creates a classic class, which is deprecated. Use native class syntax (`class Foo extends Bar {}`) instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return classicExtend(this as any, ...mixins); } /** @@ -837,9 +848,11 @@ class CoreObject { @public */ static reopen(this: C, ...args: any[]): C { - this.willReopen(); - reopen.apply(this.PrototypeMixin, args); - return this; + deprecateUntil( + '`reopen` is part of the classic class system, which is deprecated. Define the properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return classicReopen(this, ...args); } static willReopen() { @@ -921,8 +934,11 @@ class CoreObject { this: C, ...mixins: Array> ): C { - applyMixin(this, mixins); - return this; + deprecateUntil( + '`reopenClass` is part of the classic class system, which is deprecated. Define static properties and methods on a native class instead.', + DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES + ); + return classicReopenClass(this, ...mixins); } static detect(obj: unknown) { diff --git a/packages/@ember/object/index.ts b/packages/@ember/object/index.ts index 1bf0587949a..df0f6cc186b 100644 --- a/packages/@ember/object/index.ts +++ b/packages/@ember/object/index.ts @@ -10,6 +10,7 @@ import { getFactoryFor } from '@ember/-internals/container/lib/container'; import { setObservers } from '@ember/-internals/utils/lib/super'; import type { AnyFn } from '@ember/-internals/utility-types'; import CoreObject from '@ember/object/core'; +import { classicExtend } from '@ember/object/lib/classic'; import Observable from '@ember/object/observable'; export { notifyPropertyChange } from '@ember/-internals/metal/lib/property_events'; @@ -36,7 +37,7 @@ export { default as computed } from '@ember/-internals/metal/lib/computed'; */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface EmberObject extends Observable {} -class EmberObject extends CoreObject.extend(Observable) { +class EmberObject extends classicExtend(CoreObject, Observable) { get _debugContainerKey() { let factory = getFactoryFor(this); return factory !== undefined && factory.fullName; diff --git a/packages/@ember/object/lib/classic.ts b/packages/@ember/object/lib/classic.ts new file mode 100644 index 00000000000..192acfd4b6d --- /dev/null +++ b/packages/@ember/object/lib/classic.ts @@ -0,0 +1,67 @@ +/* + Internal implementations of the classic class APIs (`extend`, `reopen`, and + `reopenClass`), without the deprecation that the public methods on + `CoreObject` fire. + + Framework classes still need to build classic classes internally -- mostly to + apply framework mixins such as `Observable` or `ActionHandler` -- and there is + nothing an app author can do about that. Internal code calls these directly so + that the classic classes deprecation is only triggered by app and addon code. + + See RFC #1117: https://rfcs.emberjs.com/id/1117-deprecate-classic-classes +*/ + +import type CoreObject from '@ember/object/core'; +import Mixin, { applyMixin } from '@ember/object/mixin'; + +type EmberClassConstructor = new (...args: any[]) => T; + +type MergeArray = Arr extends [infer T, ...infer Rest] + ? T & MergeArray + : unknown; + +const reopen = Mixin.prototype.reopen; + +/* + The non-deprecated implementation behind `CoreObject.extend`. +*/ +export function classicExtend>( + Parent: Statics & EmberClassConstructor, + ...mixins: M +): Readonly & EmberClassConstructor & MergeArray; +export function classicExtend(Parent: any, ...mixins: any[]) { + let Class = class extends Parent {}; + reopen.apply((Class as unknown as typeof CoreObject).PrototypeMixin, mixins); + return Class; +} + +/* + The non-deprecated implementation behind `CoreObject.reopen`. +*/ +export function classicReopen(Class: C, ...args: any[]): C { + Class.willReopen(); + reopen.apply(Class.PrototypeMixin, args); + return Class; +} + +/* + The non-deprecated implementation behind `CoreObject.reopenClass`. +*/ +export function classicReopenClass( + Class: C, + ...mixins: Array> +): C { + applyMixin(Class, mixins); + return Class; +} + +/* + The non-deprecated implementation behind `CoreObject.prototype.reopen`. +*/ +export function classicReopenInstance( + obj: T, + ...args: Array> +): T { + applyMixin(obj, args); + return obj; +} diff --git a/packages/@ember/object/tests/classic-classes-deprecation-test.js b/packages/@ember/object/tests/classic-classes-deprecation-test.js new file mode 100644 index 00000000000..49c17f3e4a2 --- /dev/null +++ b/packages/@ember/object/tests/classic-classes-deprecation-test.js @@ -0,0 +1,90 @@ +import EmberObject from '@ember/object'; +import Mixin from '@ember/object/mixin'; +import { DEPRECATIONS } from '@ember/-internals/deprecations'; +import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; + +const { isEnabled, isRemoved } = DEPRECATIONS.DEPRECATE_CLASSIC_CLASSES; + +moduleFor( + 'Classic classes deprecation (RFC #1117)', + class extends AbstractTestCase { + [`${testUnless(isRemoved)} @test extend() is deprecated`](assert) { + let Person; + + expectDeprecation( + () => { + Person = EmberObject.extend({ name: 'Tom' }); + }, + /`\.extend\(\)` creates a classic class, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().name, 'Tom', 'the class still works'); + } + + [`${testUnless(isRemoved)} @test extend() is deprecated when applying a mixin`](assert) { + let Greeter = Mixin.create({ greet: () => 'hi' }); + let Person; + + expectDeprecation( + () => { + Person = EmberObject.extend(Greeter); + }, + /`\.extend\(\)` creates a classic class, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().greet(), 'hi', 'the mixin was still applied'); + } + + [`${testUnless(isRemoved)} @test reopen() is deprecated`](assert) { + let Person = class extends EmberObject {}; + + expectDeprecation( + () => { + Person.reopen({ name: 'Tom' }); + }, + /`reopen` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(Person.create().name, 'Tom', 'the property was still added'); + } + + [`${testUnless(isRemoved)} @test reopen() on an instance is deprecated`](assert) { + let person = class extends EmberObject {}.create(); + + expectDeprecation( + () => { + person.reopen({ name: 'Tom' }); + }, + /`reopen` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(person.name, 'Tom', 'the property was still added'); + } + + [`${testUnless(isRemoved)} @test reopenClass() is deprecated`](assert) { + let Person = class extends EmberObject {}; + + expectDeprecation( + () => { + Person.reopenClass({ species: 'Homo sapiens' }); + }, + /`reopenClass` is part of the classic class system, which is deprecated/, + isEnabled + ); + + assert.equal(Person.species, 'Homo sapiens', 'the static property was still added'); + } + + ['@test native class syntax does not trigger the deprecation'](assert) { + class Person extends EmberObject { + name = 'Tom'; + } + + assert.equal(Person.create().name, 'Tom'); + } + } +); diff --git a/packages/@ember/object/tests/computed/dependent-key-compat-test.js b/packages/@ember/object/tests/computed/dependent-key-compat-test.js index 72dc33d5eb7..b624605d60d 100644 --- a/packages/@ember/object/tests/computed/dependent-key-compat-test.js +++ b/packages/@ember/object/tests/computed/dependent-key-compat-test.js @@ -2,6 +2,7 @@ import EmberObject, { computed, observer } from '@ember/object'; import { tracked } from '@ember/-internals/metal'; import { dependentKeyCompat } from '@ember/object/compat'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'dependentKeyCompat', @@ -61,7 +62,7 @@ moduleFor( async '@test it works with async observers'(assert) { let count = 0; - let Person = EmberObject.extend({ + let Person = classicExtend(EmberObject, { firstName: tracked({ value: 'Tom' }), lastName: tracked({ value: 'Dale' }), @@ -98,7 +99,7 @@ moduleFor( '@test it does not work with sync observers'(assert) { let count = 0; - let Person = EmberObject.extend({ + let Person = classicExtend(EmberObject, { firstName: tracked({ value: 'Tom' }), lastName: tracked({ value: 'Dale' }), diff --git a/packages/@ember/object/tests/computed/reduce_computed_macros_test.js b/packages/@ember/object/tests/computed/reduce_computed_macros_test.js index 0a64fa17130..0194a08e3f3 100644 --- a/packages/@ember/object/tests/computed/reduce_computed_macros_test.js +++ b/packages/@ember/object/tests/computed/reduce_computed_macros_test.js @@ -27,6 +27,7 @@ import { collect, } from '@ember/object/computed'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; let obj; moduleFor( @@ -2007,18 +2008,16 @@ moduleFor( ['@test sort correctly when multiple sorts are chained on the same instance of a class']( assert ) { - let obj2 = klass - .extend({ - items: computed('sibling.sortedItems.[]', function () { - return this.get('sibling.sortedItems'); - }), - asdf: observer('sibling.sortedItems.[]', function () { - this.get('sibling.sortedItems'); - }), - }) - .create({ - sibling: obj, - }); + let obj2 = classicExtend(klass, { + items: computed('sibling.sortedItems.[]', function () { + return this.get('sibling.sortedItems'); + }), + asdf: observer('sibling.sortedItems.[]', function () { + this.get('sibling.sortedItems'); + }), + }).create({ + sibling: obj, + }); /* ┌───────────┐ ┌────────────┐ diff --git a/packages/@ember/object/tests/computed_test.js b/packages/@ember/object/tests/computed_test.js index 1cccb354564..4da8bee6abd 100644 --- a/packages/@ember/object/tests/computed_test.js +++ b/packages/@ember/object/tests/computed_test.js @@ -3,6 +3,7 @@ import { alias, oneWay as reads } from '@ember/object/computed'; import { A as emberA, isArray } from '@ember/array'; import EmberObject, { defineProperty, get, set, computed, observer } from '@ember/object'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend, classicReopen } from '@ember/object/lib/classic'; function K() { return this; @@ -141,7 +142,7 @@ moduleFor( } ['@test can retrieve metadata for a computed property'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { computedProperty: computed(function () {}).meta({ key: 'keyValue' }), }); @@ -178,7 +179,7 @@ moduleFor( ['@test overriding a computed property with null removes it from eachComputedProperty iteration']( assert ) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { foo: computed(function () {}), fooDidChange: observer('foo', function () {}), @@ -186,7 +187,7 @@ moduleFor( bar: computed(function () {}), }); - let SubClass = MyClass.extend({ + let SubClass = classicExtend(MyClass, { foo: null, }); @@ -202,7 +203,7 @@ moduleFor( } ['@test can iterate over a list of computed properties for a class'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { foo: computed(function () {}), fooDidChange: observer('foo', function () {}), @@ -212,11 +213,11 @@ moduleFor( qux: alias('foo'), }); - let SubClass = MyClass.extend({ + let SubClass = classicExtend(MyClass, { baz: computed(function () {}), }); - SubClass.reopen({ + classicReopen(SubClass, { bat: computed(function () {}).meta({ iAmBat: true }), }); @@ -254,7 +255,7 @@ moduleFor( ['@test list of properties updates when an additional property is added (such cache busting)']( assert ) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { foo: computed(K), fooDidChange: observer('foo', function () {}), @@ -270,7 +271,7 @@ moduleFor( assert.deepEqual(list.sort(), ['bar', 'foo'].sort(), 'expected two computed properties'); - MyClass.reopen({ + classicReopen(MyClass, { baz: computed(K), }); @@ -354,7 +355,7 @@ moduleFor( ['@test observing prop installed with computed macro reads and overriding it in create() works']( assert ) { - let Obj = EmberObject.extend({ + let Obj = classicExtend(EmberObject, { name: reads('model.name'), nameDidChange: observer('name', function () {}), }); @@ -391,7 +392,7 @@ moduleFor( } ['@test @each on maybe array'](assert) { - let Normalizer = EmberObject.extend({ + let Normalizer = classicExtend(EmberObject, { options: null, // null | undefined | { value: any } | Array<{ value: any }> // Normalize into Array @@ -487,7 +488,7 @@ moduleFor( } } - let Normalizer = EmberObject.extend({ + let Normalizer = classicExtend(EmberObject, { options: null, // null | ArrayLike<{ value: any }> // Normalize into Array diff --git a/packages/@ember/object/tests/create_test.js b/packages/@ember/object/tests/create_test.js index 0c42957e53b..1906a46aa71 100644 --- a/packages/@ember/object/tests/create_test.js +++ b/packages/@ember/object/tests/create_test.js @@ -8,6 +8,7 @@ import EmberObject, { computed, observer } from '@ember/object'; import { alias } from '@ember/object/computed'; import { buildOwner, moduleFor, runDestroy, AbstractTestCase } from 'internal-test-helpers'; import { destroy } from '@glimmer/destroyable'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'EmberObject.create', @@ -40,7 +41,7 @@ moduleFor( } ['@test calls computed property setters'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { foo: computed({ get() { return "this is not the value you're looking for"; @@ -57,7 +58,7 @@ moduleFor( ['@test sets up mandatory setters for simple properties watched with observers'](assert) { if (DEBUG) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { foo: null, bar: null, fooDidChange: observer('foo', function () {}), @@ -130,7 +131,7 @@ moduleFor( ['@test does not sets up separate mandatory setters on getters'](assert) { if (DEBUG) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { get foo() { return 'bar'; }, diff --git a/packages/@ember/object/tests/destroy_test.js b/packages/@ember/object/tests/destroy_test.js index eea3ccdd957..97ba539a3c4 100644 --- a/packages/@ember/object/tests/destroy_test.js +++ b/packages/@ember/object/tests/destroy_test.js @@ -4,6 +4,7 @@ import { peekMeta } from '@ember/-internals/meta'; import EmberObject, { get, set, observer } from '@ember/object'; import { DEBUG } from '@glimmer/env'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( '@ember/-internals/runtime/system/object/destroy_test', @@ -31,7 +32,7 @@ moduleFor( assert ) { if (DEBUG) { - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { fooDidChange: observer('foo', function () {}), }).create({ foo: 'bar', @@ -47,7 +48,7 @@ moduleFor( async ['@test observers should not fire after an object has been destroyed'](assert) { let count = 0; - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { fooDidChange: observer('foo', function () { count++; }), @@ -75,7 +76,7 @@ moduleFor( let objs = {}; - let A = EmberObject.extend({ + let A = classicExtend(EmberObject, { objs: objs, isAlive: true, willDestroy() { @@ -89,7 +90,7 @@ moduleFor( }), }); - let B = EmberObject.extend({ + let B = classicExtend(EmberObject, { objs: objs, isAlive: true, willDestroy() { @@ -103,7 +104,7 @@ moduleFor( }), }); - let C = EmberObject.extend({ + let C = classicExtend(EmberObject, { objs: objs, isAlive: true, willDestroy() { @@ -117,7 +118,7 @@ moduleFor( }), }); - let LongLivedObject = EmberObject.extend({ + let LongLivedObject = classicExtend(EmberObject, { objs: objs, isAliveDidChange: observer('objs.a.isAlive', function () { shouldChange++; diff --git a/packages/@ember/object/tests/es-compatibility-test.js b/packages/@ember/object/tests/es-compatibility-test.js index 35372ca5f70..a147289d3c4 100644 --- a/packages/@ember/object/tests/es-compatibility-test.js +++ b/packages/@ember/object/tests/es-compatibility-test.js @@ -17,6 +17,7 @@ import { testUnless, } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; +import { classicExtend, classicReopen } from '@ember/object/lib/classic'; moduleFor( 'EmberObject ES Compatibility', @@ -183,7 +184,7 @@ moduleFor( property2: 'data-2', }); - class MyObject extends EmberObject.extend(Mixin1, Mixin2) {} + class MyObject extends classicExtend(EmberObject, Mixin1, Mixin2) {} let myObject = MyObject.create(); assert.equal(myObject.property1, 'data-1', 'includes the first mixin'); @@ -296,7 +297,7 @@ moduleFor( expectDeprecation( () => { A = class extends ( - EmberObject.extend({ + classicExtend(EmberObject, { fooDidChange: observer('foo', function () { fooDidChangeBase++; }), @@ -410,7 +411,7 @@ moduleFor( }, }); - class B extends A.extend(Mixin1, Mixin2) { + class B extends classicExtend(A, Mixin1, Mixin2) { init() { calls.push('B init before super.init'); super.init(...arguments); @@ -443,7 +444,7 @@ moduleFor( // Only string listeners are allowed for prototypes addListener(B.prototype, 'someEvent', null, 'onSomeEvent'); - B.reopen({ + classicReopen(B, { init() { calls.push('reopen init before _super'); this._super(...arguments); diff --git a/packages/@ember/object/tests/evented_test.js b/packages/@ember/object/tests/evented_test.js index 6e2831d95da..815c0b654d5 100644 --- a/packages/@ember/object/tests/evented_test.js +++ b/packages/@ember/object/tests/evented_test.js @@ -6,6 +6,7 @@ import Route from '@ember/routing/route'; import EmberRouter from '@ember/routing/router'; import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Ember.Evented', @@ -31,7 +32,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test works properly on proxy-ish objects`](assert) { let eventedProxyObj; - eventedProxyObj = class extends CoreObject.extend(EventedMixin) { + eventedProxyObj = class extends classicExtend(CoreObject, EventedMixin) { unknownProperty() { return true; } diff --git a/packages/@ember/object/tests/events_test.js b/packages/@ember/object/tests/events_test.js index c9c600060c6..c790495413e 100644 --- a/packages/@ember/object/tests/events_test.js +++ b/packages/@ember/object/tests/events_test.js @@ -2,6 +2,7 @@ import EmberObject from '@ember/object'; import Evented from '@ember/object/evented'; import { moduleFor, AbstractTestCase, expectDeprecation, testUnless } from 'internal-test-helpers'; import { DEPRECATIONS } from '../../-internals/deprecations'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Object events', @@ -15,7 +16,7 @@ moduleFor( }; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); expectDeprecation( () => { @@ -56,7 +57,7 @@ moduleFor( }; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); expectDeprecation( () => { @@ -93,7 +94,7 @@ moduleFor( let self, args; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); expectDeprecation( () => { @@ -125,7 +126,7 @@ moduleFor( let count = 0; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); expectDeprecation( () => { @@ -170,7 +171,7 @@ moduleFor( let self, args; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); let target = {}; expectDeprecation( @@ -208,7 +209,7 @@ moduleFor( }; let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); expectDeprecation( () => { @@ -243,7 +244,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test a listener registered with one can be removed with off`](assert) { let obj; - obj = class extends EmberObject.extend(Evented) { + obj = class extends classicExtend(EmberObject, Evented) { F() {} }.create(); let F = function () {}; @@ -305,7 +306,7 @@ moduleFor( DEPRECATIONS.DEPRECATE_EVENTED.isRemoved )} @test adding and removing listeners should be chainable`](assert) { let obj; - obj = EmberObject.extend(Evented).create(); + obj = classicExtend(EmberObject, Evented).create(); let F = function () {}; let ret; diff --git a/packages/@ember/object/tests/extend_test.js b/packages/@ember/object/tests/extend_test.js index a99259276a7..78f7fd4e201 100644 --- a/packages/@ember/object/tests/extend_test.js +++ b/packages/@ember/object/tests/extend_test.js @@ -1,27 +1,28 @@ import { computed, get } from '@ember/object'; import EmberObject, { observer } from '@ember/object'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'EmberObject.extend', class extends AbstractTestCase { ['@test Basic extend'](assert) { - let SomeClass = EmberObject.extend({ foo: 'BAR' }); + let SomeClass = classicExtend(EmberObject, { foo: 'BAR' }); assert.ok(SomeClass.isClass, 'A class has isClass of true'); let obj = SomeClass.create(); assert.equal(obj.foo, 'BAR'); } ['@test Sub-subclass'](assert) { - let SomeClass = EmberObject.extend({ foo: 'BAR' }); - let AnotherClass = SomeClass.extend({ bar: 'FOO' }); + let SomeClass = classicExtend(EmberObject, { foo: 'BAR' }); + let AnotherClass = classicExtend(SomeClass, { bar: 'FOO' }); let obj = AnotherClass.create(); assert.equal(obj.foo, 'BAR'); assert.equal(obj.bar, 'FOO'); } ['@test Overriding a method several layers deep'](assert) { - let SomeClass = EmberObject.extend({ + let SomeClass = classicExtend(EmberObject, { fooCnt: 0, foo() { this.fooCnt++; @@ -33,7 +34,7 @@ moduleFor( }, }); - let AnotherClass = SomeClass.extend({ + let AnotherClass = classicExtend(SomeClass, { barCnt: 0, bar() { this.barCnt++; @@ -41,7 +42,7 @@ moduleFor( }, }); - let FinalClass = AnotherClass.extend({ + let FinalClass = classicExtend(AnotherClass, { fooCnt: 0, foo() { this.fooCnt++; @@ -56,7 +57,7 @@ moduleFor( assert.equal(obj.barCnt, 2, 'should invoke both'); // Try overriding on create also - obj = FinalClass.extend({ + obj = classicExtend(FinalClass, { foo() { this.fooCnt++; this._super(...arguments); @@ -70,12 +71,12 @@ moduleFor( } ['@test With concatenatedProperties'](assert) { - let SomeClass = EmberObject.extend({ + let SomeClass = classicExtend(EmberObject, { things: 'foo', concatenatedProperties: ['things'], }); - let AnotherClass = SomeClass.extend({ things: 'bar' }); - let YetAnotherClass = SomeClass.extend({ things: 'baz' }); + let AnotherClass = classicExtend(SomeClass, { things: 'bar' }); + let YetAnotherClass = classicExtend(SomeClass, { things: 'baz' }); let some = SomeClass.create(); let another = AnotherClass.create(); let yetAnother = YetAnotherClass.create(); @@ -93,15 +94,15 @@ moduleFor( } ['@test With concatenatedProperties class properties'](assert) { - let SomeClass = EmberObject.extend(); - SomeClass.reopenClass({ + let SomeClass = classicExtend(EmberObject); + classicReopenClass(SomeClass, { concatenatedProperties: ['things'], things: 'foo', }); - let AnotherClass = SomeClass.extend(); - AnotherClass.reopenClass({ things: 'bar' }); - let YetAnotherClass = SomeClass.extend(); - YetAnotherClass.reopenClass({ things: 'baz' }); + let AnotherClass = classicExtend(SomeClass); + classicReopenClass(AnotherClass, { things: 'bar' }); + let YetAnotherClass = classicExtend(SomeClass); + classicReopenClass(YetAnotherClass, { things: 'baz' }); let some = SomeClass.create(); let another = AnotherClass.create(); let yetAnother = YetAnotherClass.create(); @@ -123,7 +124,7 @@ moduleFor( } async ['@test Overriding a computed property with an observer'](assert) { - let Parent = EmberObject.extend({ + let Parent = classicExtend(EmberObject, { foo: computed(function () { return 'FOO'; }), @@ -131,7 +132,7 @@ moduleFor( let seen = []; - let Child = Parent.extend({ + let Child = classicExtend(Parent, { foo: observer('bar', function () { seen.push(this.get('bar')); }), diff --git a/packages/@ember/object/tests/mixin/reopen_test.js b/packages/@ember/object/tests/mixin/reopen_test.js index 1a7b0f263a2..e78cd079bb1 100644 --- a/packages/@ember/object/tests/mixin/reopen_test.js +++ b/packages/@ember/object/tests/mixin/reopen_test.js @@ -2,6 +2,7 @@ import EmberObject, { get } from '@ember/object'; import Mixin from '@ember/object/mixin'; import { run } from '@ember/runloop'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopen } from '@ember/object/lib/classic'; moduleFor( 'Mixin#reopen', @@ -30,7 +31,7 @@ moduleFor( } }; - Taco.reopen({ + classicReopen(Taco, { createBreakfast() { return this._super(...arguments); }, diff --git a/packages/@ember/object/tests/observable_test.js b/packages/@ember/object/tests/observable_test.js index 8b6ce290204..43c1663d6f9 100644 --- a/packages/@ember/object/tests/observable_test.js +++ b/packages/@ember/object/tests/observable_test.js @@ -5,6 +5,7 @@ import EmberObject, { observer } from '@ember/object'; import Observable from '@ember/object/observable'; import { A as emberA } from '@ember/array'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests @@ -33,7 +34,7 @@ import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpe let object, objectA, objectB, objectC, objectD, objectE, objectF, lookup; -const ObservableObject = EmberObject.extend(Observable); +const ObservableObject = classicExtend(EmberObject, Observable); const originalLookup = context.lookup; class ObservableTestCase extends AbstractTestCase { @@ -58,7 +59,7 @@ moduleFor( 'object.get()', class extends ObservableTestCase { beforeEach() { - object = ObservableObject.extend(Observable, { + object = classicExtend(ObservableObject, Observable, { computed: computed(function () { return 'value'; }), @@ -107,7 +108,7 @@ moduleFor( 'Ember.get()', class extends ObservableTestCase { beforeEach() { - objectA = ObservableObject.extend({ + objectA = classicExtend(ObservableObject, { computed: computed(function () { return 'value'; }), @@ -174,7 +175,7 @@ moduleFor( class extends ObservableTestCase { ['@test should return a property at a given path relative to the passed object'](assert) { let foo = ObservableObject.create({ - bar: ObservableObject.extend({ + bar: classicExtend(ObservableObject, { baz: computed(function () { return 'blargh'; }), @@ -206,7 +207,7 @@ moduleFor( 'object.set()', class extends ObservableTestCase { beforeEach() { - object = ObservableObject.extend({ + object = classicExtend(ObservableObject, { computed: computed({ get() { return this._computed; @@ -297,7 +298,7 @@ moduleFor( beforeEach() { lookup = context.lookup = {}; - object = ObservableObject.extend({ + object = classicExtend(ObservableObject, { computed: computed({ get() { this.computedCalls.push('getter-called'); @@ -508,7 +509,7 @@ moduleFor( } ['@test dependent keys should be able to be specified as property paths'](assert) { - let depObj = ObservableObject.extend({ + let depObj = classicExtend(ObservableObject, { menuPrice: computed('menu.price', function () { return this.get('menu.price'); }), @@ -535,7 +536,7 @@ moduleFor( let DepObj; run(function () { - lookup.DepObj = DepObj = ObservableObject.extend({ + lookup.DepObj = DepObj = classicExtend(ObservableObject, { price: computed('restaurant.menu.price', function () { return this.get('restaurant.menu.price'); }), @@ -614,7 +615,7 @@ moduleFor( 'Observable objects & object properties ', class extends ObservableTestCase { beforeEach() { - object = ObservableObject.extend({ + object = classicExtend(ObservableObject, { getEach() { let keys = ['normal', 'abnormal']; let ret = []; diff --git a/packages/@ember/object/tests/observer_test.js b/packages/@ember/object/tests/observer_test.js index 3e181e2e007..7d022ddb16b 100644 --- a/packages/@ember/object/tests/observer_test.js +++ b/packages/@ember/object/tests/observer_test.js @@ -2,12 +2,13 @@ import { run } from '@ember/runloop'; import { alias } from '@ember/-internals/metal'; import EmberObject, { get, set, observer } from '@ember/object'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'EmberObject observer', class extends AbstractTestCase { async ['@test observer on class'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { count: 0, foo: observer('bar', function () { @@ -27,7 +28,7 @@ moduleFor( } async ['@test setting `undefined` value on observed property behaves correctly'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { mood: 'good', foo: observer('mood', function () {}), }); @@ -54,7 +55,7 @@ moduleFor( } async ['@test observer on subclass'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { count: 0, foo: observer('bar', function () { @@ -62,7 +63,7 @@ moduleFor( }), }); - let Subclass = MyClass.extend({ + let Subclass = classicExtend(MyClass, { foo: observer('baz', function () { set(this, 'count', get(this, 'count') + 1); }), @@ -85,7 +86,7 @@ moduleFor( } async ['@test observer on instance'](assert) { - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { foo: observer('bar', function () { set(this, 'count', get(this, 'count') + 1); }), @@ -105,7 +106,7 @@ moduleFor( } async ['@test observer on instance overriding class'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { count: 0, foo: observer('bar', function () { @@ -113,7 +114,7 @@ moduleFor( }), }); - let obj = MyClass.extend({ + let obj = classicExtend(MyClass, { foo: observer('baz', function () { // <-- change property we observe set(this, 'count', get(this, 'count') + 1); @@ -136,7 +137,7 @@ moduleFor( } async ['@test observer should not fire after being destroyed'](assert) { - let obj = EmberObject.extend({ + let obj = classicExtend(EmberObject, { count: 0, foo: observer('bar', function () { set(this, 'count', get(this, 'count') + 1); @@ -161,7 +162,7 @@ moduleFor( // async ['@test chain observer on class'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { count: 0, foo: observer('bar.baz', function () { @@ -197,7 +198,7 @@ moduleFor( } async ['@test clobbering a chain observer on subclass'](assert) { - let MyClass = EmberObject.extend({ + let MyClass = classicExtend(EmberObject, { count: 0, foo: observer('bar.baz', function () { @@ -205,11 +206,11 @@ moduleFor( }), }); - let obj1 = MyClass.extend().create({ + let obj1 = classicExtend(MyClass).create({ bar: { baz: 'biff' }, }); - let obj2 = MyClass.extend({ + let obj2 = classicExtend(MyClass, { foo: observer('bar2.baz', function () { set(this, 'count', get(this, 'count') + 1); }), @@ -248,14 +249,14 @@ moduleFor( ) { let changed = false; - let ChildClass = EmberObject.extend({ + let ChildClass = classicExtend(EmberObject, { parent: null, parentOneTwoDidChange: observer('parent.one.two', function () { changed = true; }), }); - let ParentClass = EmberObject.extend({ + let ParentClass = classicExtend(EmberObject, { one: { two: 'old', }, @@ -287,7 +288,7 @@ moduleFor( async ['@test cannot re-enter observer while it is flushing'](assert) { let changed = false; - let Class = EmberObject.extend({ + let Class = classicExtend(EmberObject, { bar: 0, get foo() { diff --git a/packages/@ember/object/tests/reopenClass_test.js b/packages/@ember/object/tests/reopenClass_test.js index 76d562a2076..59aa3ca17a6 100644 --- a/packages/@ember/object/tests/reopenClass_test.js +++ b/packages/@ember/object/tests/reopenClass_test.js @@ -1,13 +1,14 @@ import { get } from '@ember/object'; import EmberObject from '@ember/object'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopenClass } from '@ember/object/lib/classic'; moduleFor( 'system/object/reopenClass', class extends AbstractTestCase { ['@test adds new properties to subclass'](assert) { let Subclass = class extends EmberObject {}; - Subclass.reopenClass({ + classicReopenClass(Subclass, { foo() { return 'FOO'; }, @@ -20,7 +21,7 @@ moduleFor( ['@test class properties inherited by subclasses'](assert) { let Subclass = class extends EmberObject {}; - Subclass.reopenClass({ + classicReopenClass(Subclass, { foo() { return 'FOO'; }, diff --git a/packages/@ember/object/tests/reopen_test.js b/packages/@ember/object/tests/reopen_test.js index a68e236fb42..bfe038a9411 100644 --- a/packages/@ember/object/tests/reopen_test.js +++ b/packages/@ember/object/tests/reopen_test.js @@ -1,5 +1,6 @@ import EmberObject, { get } from '@ember/object'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopen } from '@ember/object/lib/classic'; // TODO: Update these tests (or the title) to match each other. moduleFor( @@ -7,7 +8,7 @@ moduleFor( class extends AbstractTestCase { ['@test adds new properties to subclass instance'](assert) { let Subclass = class extends EmberObject {}; - Subclass.reopen({ + classicReopen(Subclass, { foo() { return 'FOO'; }, @@ -22,7 +23,7 @@ moduleFor( let Subclass = class extends EmberObject {}; let SubSub = class extends Subclass {}; - Subclass.reopen({ + classicReopen(Subclass, { foo() { return 'FOO'; }, @@ -38,7 +39,7 @@ moduleFor( Subclass.create(); - Subclass.reopen({ + classicReopen(Subclass, { trololol: true, }); diff --git a/packages/@ember/routing/lib/routing-service.ts b/packages/@ember/routing/lib/routing-service.ts index 610fcdac4e9..f8ca765834d 100644 --- a/packages/@ember/routing/lib/routing-service.ts +++ b/packages/@ember/routing/lib/routing-service.ts @@ -11,6 +11,7 @@ import type Route from '@ember/routing/route'; import EmberRouter from '@ember/routing/router'; import type RouterState from './router_state'; import { ROUTER } from '@ember/routing/router-service'; +import { classicReopen } from '@ember/object/lib/classic'; /** The Routing service is used by LinkTo, and provides facilities for @@ -128,7 +129,7 @@ export default class RoutingService extends Service { } } -RoutingService.reopen({ +classicReopen(RoutingService, { targetState: readOnly('router.targetState'), currentState: readOnly('router.currentState'), currentRouteName: readOnly('router.currentRouteName'), diff --git a/packages/@ember/routing/none-location.ts b/packages/@ember/routing/none-location.ts index e396d8ef335..cce8583c504 100644 --- a/packages/@ember/routing/none-location.ts +++ b/packages/@ember/routing/none-location.ts @@ -2,6 +2,7 @@ import EmberObject from '@ember/object'; import { assert } from '@ember/debug'; import type { default as EmberLocation, UpdateCallback } from '@ember/routing/location'; import { escapeRegExp } from './lib/location-utils'; +import { classicReopen } from '@ember/object/lib/classic'; /** @module @ember/routing/none-location @@ -127,7 +128,7 @@ export default class NoneLocation extends EmberObject implements EmberLocation { } } -NoneLocation.reopen({ +classicReopen(NoneLocation, { path: '', rootURL: '/', }); diff --git a/packages/@ember/routing/route.ts b/packages/@ember/routing/route.ts index dd553d3a16e..e2e2c518a01 100644 --- a/packages/@ember/routing/route.ts +++ b/packages/@ember/routing/route.ts @@ -49,6 +49,7 @@ import { prefixRouteNameArg, stashParamNames, } from './lib/utils'; +import { classicExtend, classicReopen } from '@ember/object/lib/classic'; export interface ExtendedInternalRouteInfo extends InternalRouteInfo { _names?: unknown[]; @@ -265,7 +266,7 @@ interface Route extends IRoute, ActionHandler { error?(error: Error, transition: Transition): boolean | void; } -class Route extends EmberObject.extend(ActionHandler) implements IRoute { +class Route extends classicExtend(EmberObject, ActionHandler) implements IRoute { static { // The deprecated Evented mixin is no longer applied, but instances still // provide its methods, so `Evented.detect` must keep returning true. @@ -2095,7 +2096,7 @@ export function hasDefaultSerialize(route: Route): boolean { } // Set these here so they can be overridden with extend -Route.reopen({ +classicReopen(Route, { mergedProperties: ['queryParams'], queryParams: {}, templateName: null, diff --git a/packages/@ember/routing/router.ts b/packages/@ember/routing/router.ts index a057ae2e5ea..801c1dc1fd2 100644 --- a/packages/@ember/routing/router.ts +++ b/packages/@ember/routing/router.ts @@ -66,6 +66,7 @@ import { eventedOff, eventedHas, } from '@ember/-internals/metal/lib/evented-methods'; +import { classicReopen, classicReopenClass } from '@ember/object/lib/classic'; /** @module @ember/routing/router @@ -304,7 +305,7 @@ class EmberRouter extends EmberObject { if (!this.dslCallbacks) { this.dslCallbacks = []; // FIXME: Can we remove this? - this.reopenClass({ dslCallbacks: this.dslCallbacks }); + classicReopenClass(this, { dslCallbacks: this.dslCallbacks }); } this.dslCallbacks.push(callback); @@ -1864,7 +1865,7 @@ function forEachQueryParam( } } -EmberRouter.reopen({ +classicReopen(EmberRouter, { didTransition: defaultDidTransition, willTransition: defaultWillTransition, rootURL: '/', diff --git a/packages/@ember/routing/tests/location/history_location_test.js b/packages/@ember/routing/tests/location/history_location_test.js index 68505a5a3ec..e4dbd0ef965 100644 --- a/packages/@ember/routing/tests/location/history_location_test.js +++ b/packages/@ember/routing/tests/location/history_location_test.js @@ -2,6 +2,7 @@ import { run } from '@ember/runloop'; import { set } from '@ember/object'; import HistoryLocation from '@ember/routing/history-location'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopen } from '@ember/object/lib/classic'; let FakeHistory, HistoryTestLocation, location; @@ -68,7 +69,7 @@ moduleFor( ['@test HistoryLocation initState does not get fired on init'](assert) { assert.expect(1); - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { assert.ok(true, 'init was called'); this._super(...arguments); @@ -120,7 +121,7 @@ moduleFor( ['@test base URL is removed when retrieving the current pathname'](assert) { assert.expect(1); - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); @@ -142,7 +143,7 @@ moduleFor( ['@test base URL is preserved when moving around'](assert) { assert.expect(2); - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); @@ -184,7 +185,7 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); @@ -202,7 +203,7 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, does not remove rootURL if its not at start of url']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); @@ -219,7 +220,7 @@ moduleFor( ['@test HistoryLocation.getURL() will not remove the rootURL when only a partial match']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/bars/baz')); @@ -235,7 +236,7 @@ moduleFor( ['@test HistoryLocation.getURL() returns the current url, does not remove baseURL if its not at start of url']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); @@ -252,7 +253,7 @@ moduleFor( ['@test HistoryLocation.getURL() will not remove the baseURL when only a partial match']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/bars/baz')); @@ -266,7 +267,7 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.search'](assert) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin')); @@ -279,7 +280,7 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.hash'](assert) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar#pink-power-ranger')); @@ -292,7 +293,7 @@ moduleFor( } ['@test HistoryLocation.getURL() includes location.hash and location.search'](assert) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/foo/bar?time=morphin#pink-power-ranger')); @@ -305,7 +306,7 @@ moduleFor( } ['@test HistoryLocation.getURL() drops duplicate slashes'](assert) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); let location = mockBrowserLocation('//admin//profile//'); @@ -322,7 +323,7 @@ moduleFor( ['@test HistoryLocation.getURL() treats regex metacharacters in rootURL and baseURL literally']( assert ) { - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/axc/secret')); @@ -344,7 +345,7 @@ moduleFor( FakeHistory.state = existingState; - HistoryTestLocation.reopen({ + classicReopen(HistoryTestLocation, { init() { this._super(...arguments); set(this, 'location', mockBrowserLocation('/route/path')); diff --git a/packages/@ember/routing/tests/location/none_location_test.js b/packages/@ember/routing/tests/location/none_location_test.js index 04f77db057b..e6d8854f007 100644 --- a/packages/@ember/routing/tests/location/none_location_test.js +++ b/packages/@ember/routing/tests/location/none_location_test.js @@ -2,6 +2,7 @@ import { run } from '@ember/runloop'; import { set } from '@ember/object'; import NoneLocation from '@ember/routing/none-location'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopen } from '@ember/object/lib/classic'; let NoneTestLocation, location; @@ -29,7 +30,7 @@ moduleFor( } ['@test NoneLocation.formatURL() returns the current url always appending rootURL'](assert) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/en/'); @@ -42,7 +43,7 @@ moduleFor( } ['@test NoneLocation.getURL() returns the current path minus rootURL'](assert) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/foo/'); @@ -58,7 +59,7 @@ moduleFor( ['@test NoneLocation.getURL() will remove the rootURL only from the beginning of a url']( assert ) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/bar/'); @@ -72,7 +73,7 @@ moduleFor( } ['@test NoneLocation.getURL() will not remove the rootURL when only a partial match'](assert) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/bar/'); @@ -86,7 +87,7 @@ moduleFor( } ['@test NoneLocation.getURL() treats regex metacharacters in rootURL literally'](assert) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/a.c/'); @@ -100,7 +101,7 @@ moduleFor( } ['@test NoneLocation.getURL() strips the rootURL when it has an extra trailing slash'](assert) { - NoneTestLocation.reopen({ + classicReopen(NoneTestLocation, { init() { this._super(...arguments); set(this, 'rootURL', '/foo//'); diff --git a/packages/@ember/routing/tests/system/dsl_test.js b/packages/@ember/routing/tests/system/dsl_test.js index 5b7d63df119..0e4083eebc6 100644 --- a/packages/@ember/routing/tests/system/dsl_test.js +++ b/packages/@ember/routing/tests/system/dsl_test.js @@ -1,5 +1,6 @@ import EmberRouter from '@ember/routing/router'; import { buildOwner, moduleFor, runDestroy, AbstractTestCase } from 'internal-test-helpers'; +import { classicReopenInstance } from '@ember/object/lib/classic'; moduleFor( 'Ember Router DSL', @@ -87,7 +88,7 @@ moduleFor( this.route('blork'); }); - this.routerInstance.reopen({ + classicReopenInstance(this.routerInstance, { _hasModuleBasedResolver() { return true; }, @@ -133,7 +134,7 @@ moduleFor( }); }); - this.routerInstance.reopen({ + classicReopenInstance(this.routerInstance, { _hasModuleBasedResolver() { return true; }, @@ -285,7 +286,7 @@ moduleFor( this.mount('chat'); }); - this.routerInstance.reopen({ + classicReopenInstance(this.routerInstance, { _hasModuleBasedResolver() { return true; }, @@ -304,7 +305,7 @@ moduleFor( this.mount('chat', { as: 'shoutbox' }); }); - this.routerInstance.reopen({ + classicReopenInstance(this.routerInstance, { _hasModuleBasedResolver() { return true; }, @@ -351,7 +352,7 @@ moduleFor( }); }); - this.routerInstance.reopen({ + classicReopenInstance(this.routerInstance, { _hasModuleBasedResolver() { return true; }, diff --git a/packages/@ember/utils/tests/compare_test.js b/packages/@ember/utils/tests/compare_test.js index c07be94af20..567a7f4ca9b 100644 --- a/packages/@ember/utils/tests/compare_test.js +++ b/packages/@ember/utils/tests/compare_test.js @@ -1,11 +1,12 @@ import { compare, typeOf } from '@ember/utils'; import EmberObject from '@ember/object'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend, classicReopenClass } from '@ember/object/lib/classic'; let data = []; -let Comp = EmberObject.extend(); +let Comp = classicExtend(EmberObject); -Comp.reopenClass({ +classicReopenClass(Comp, { compare(obj) { return obj.get('val'); }, diff --git a/packages/@ember/utils/tests/type_of_test.js b/packages/@ember/utils/tests/type_of_test.js index f63116620b0..22425157226 100644 --- a/packages/@ember/utils/tests/type_of_test.js +++ b/packages/@ember/utils/tests/type_of_test.js @@ -2,6 +2,7 @@ import { typeOf } from '@ember/utils'; import EmberObject from '@ember/object'; import { window } from '@ember/-internals/browser-environment'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Ember Type Checking', @@ -40,7 +41,7 @@ moduleFor( assert.equal(typeOf(instance), 'instance', 'item of type instance'); assert.equal(typeOf(instance.method), 'function', 'item of type function'); assert.equal(typeOf(instance.asyncMethod), 'function', 'item of type async function'); - assert.equal(typeOf(EmberObject.extend()), 'class', 'item of type class'); + assert.equal(typeOf(classicExtend(EmberObject)), 'class', 'item of type class'); assert.equal(typeOf(new Error()), 'error', 'item of type error'); } diff --git a/packages/ember-testing/lib/adapters/adapter.ts b/packages/ember-testing/lib/adapters/adapter.ts index 26876da9d42..65963bdae2f 100644 --- a/packages/ember-testing/lib/adapters/adapter.ts +++ b/packages/ember-testing/lib/adapters/adapter.ts @@ -1,4 +1,5 @@ import EmberObject from '@ember/object'; +import { classicExtend } from '@ember/object/lib/classic'; /** @module @ember/test @@ -16,7 +17,7 @@ interface Adapter extends EmberObject { asyncEnd(): void; exception(error: unknown): never; } -const Adapter = EmberObject.extend({ +const Adapter = classicExtend(EmberObject, { /** This callback will be called whenever an async operation is about to start. diff --git a/packages/ember/tests/routing/decoupled_basic_test.js b/packages/ember/tests/routing/decoupled_basic_test.js index 4b53915cb78..9c5f48f9716 100644 --- a/packages/ember/tests/routing/decoupled_basic_test.js +++ b/packages/ember/tests/routing/decoupled_basic_test.js @@ -21,6 +21,7 @@ import { addObserver } from '@ember/-internals/metal'; import { service } from '@ember/service'; import Engine from '@ember/engine'; import { InternalTransition as Transition } from 'router_js'; +import { classicReopen } from '@ember/object/lib/classic'; let originalConsoleError; @@ -345,7 +346,7 @@ moduleFor( ['@test using replaceWith calls location.replaceURL if available'](assert) { let setCount = 0; let replaceCount = 0; - this.router.reopen({ + classicReopen(this.router, { location: NoneLocation.create({ setURL(path) { setCount++; @@ -380,7 +381,7 @@ moduleFor( ['@test using replaceWith calls setURL if location.replaceURL is not defined'](assert) { let setCount = 0; - this.router.reopen({ + classicReopen(this.router, { location: NoneLocation.create({ setURL(path) { setCount++; @@ -655,7 +656,7 @@ moduleFor( }, }); - this.router.reopen({ + classicReopen(this.router, { // location: 'historyTest', location, rootURL: rootURL, @@ -700,7 +701,7 @@ moduleFor( } ); - this.router.reopen({ + classicReopen(this.router, { location: 'history-test', rootURL: rootURL, // if we transition in this test we will receive failures diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index 76a0214c0ed..394db31a6bc 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -12,6 +12,7 @@ import { service } from '@ember/service'; import { precompileTemplate } from '@ember/template-compilation'; import { QueryParamTestCase, moduleFor, getTextOf, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Query Params - main', @@ -1636,7 +1637,7 @@ moduleFor( this.add( 'route:application', - Route.extend({ + classicExtend(Route, { queryParams: [{ commitBy: { replace: true } }], }) ); diff --git a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js index c369825e7c9..6867e165cbb 100644 --- a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js @@ -4,6 +4,7 @@ import Route from '@ember/routing/route'; import { computed } from '@ember/object'; import { precompileTemplate } from '@ember/template-compilation'; import { QueryParamTestCase, moduleFor, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend, classicReopen } from '@ember/object/lib/classic'; class ModelDependentQPTestCase extends QueryParamTestCase { boot() { @@ -20,11 +21,11 @@ class ModelDependentQPTestCase extends QueryParamTestCase { } reopenController(name, options) { - this.application.resolveRegistration(`controller:${name}`).reopen(options); + classicReopen(this.application.resolveRegistration(`controller:${name}`), options); } reopenRoute(name, options) { - this.application.resolveRegistration(`route:${name}`).reopen(options); + classicReopen(this.application.resolveRegistration(`route:${name}`), options); } } @@ -69,7 +70,7 @@ moduleFor( this.add( 'controller:article', - Controller.extend({ + classicExtend(Controller, { queryParams: ['q', 'z'], q: 'wat', z: 0, @@ -78,7 +79,7 @@ moduleFor( this.add( 'controller:comments', - Controller.extend({ + classicExtend(Controller, { queryParams: 'page', page: 1, }) @@ -415,7 +416,7 @@ moduleFor( this.add( 'controller:site.article', - Controller.extend({ + classicExtend(Controller, { queryParams: ['q', 'z'], q: 'wat', z: 0, @@ -424,7 +425,7 @@ moduleFor( this.add( 'controller:site.article.comments', - Controller.extend({ + classicExtend(Controller, { queryParams: 'page', page: 1, }) @@ -800,7 +801,7 @@ moduleFor( this.add( 'controller:site', - Controller.extend({ + classicExtend(Controller, { queryParams: ['country'], country: 'au', }) @@ -808,7 +809,7 @@ moduleFor( this.add( 'controller:site.article', - Controller.extend({ + classicExtend(Controller, { queryParams: ['q', 'z'], q: 'wat', z: 0, @@ -817,7 +818,7 @@ moduleFor( this.add( 'controller:site.article.comments', - Controller.extend({ + classicExtend(Controller, { queryParams: ['page'], page: 1, }) diff --git a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js index b282c8f910d..3d9b51811e0 100644 --- a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js @@ -2,6 +2,7 @@ import Controller from '@ember/controller'; import Route from '@ember/routing/route'; import Mixin from '@ember/object/mixin'; import { QueryParamTestCase, moduleFor, runLoopSettled } from 'internal-test-helpers'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Query Params - overlapping query param property names', @@ -116,7 +117,7 @@ moduleFor( ) { assert.expect(1); - let parentController = Controller.extend({ + let parentController = classicExtend(Controller, { queryParams: { page: 'page' }, }); this.add('controller:parent', parentController); @@ -157,12 +158,12 @@ moduleFor( this.add( 'controller:parent', - Controller.extend(HasPage, { + classicExtend(Controller, HasPage, { queryParams: { page: 'yespage' }, }) ); - this.add('controller:parent.child', Controller.extend(HasPage)); + this.add('controller:parent.child', classicExtend(Controller, HasPage)); await this.setupBase(); this.assertCurrentPath('/parent/child'); diff --git a/packages/ember/tests/routing/router_service_test/basic_test.js b/packages/ember/tests/routing/router_service_test/basic_test.js index aa5cc812eea..ba8392bc1ec 100644 --- a/packages/ember/tests/routing/router_service_test/basic_test.js +++ b/packages/ember/tests/routing/router_service_test/basic_test.js @@ -3,6 +3,7 @@ import NoneLocation from '@ember/routing/none-location'; import { set } from '@ember/object'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; import { service } from '@ember/service'; +import { classicReopen } from '@ember/object/lib/classic'; moduleFor( 'Router Service - main', @@ -156,7 +157,7 @@ moduleFor( ['@test RouterService can be injected into router and accessed on init'](assert) { assert.expect(1); - this.router.reopen({ + classicReopen(this.router, { routerService: service('router'), init() { this.routerService.one('routeDidChange', () => { diff --git a/packages/ember/tests/routing/router_service_test/recognize_test.js b/packages/ember/tests/routing/router_service_test/recognize_test.js index 19674c280dd..3532263288a 100644 --- a/packages/ember/tests/routing/router_service_test/recognize_test.js +++ b/packages/ember/tests/routing/router_service_test/recognize_test.js @@ -1,6 +1,7 @@ import { RouterTestCase, moduleFor } from 'internal-test-helpers'; import { precompileTemplate } from '@ember/template-compilation'; import Route from '@ember/routing/route'; +import { classicReopen } from '@ember/object/lib/classic'; moduleFor( 'Router Service - recognize', @@ -33,7 +34,7 @@ moduleFor( } '@test respects the usage of a different rootURL'(assert) { - this.router.reopen({ + classicReopen(this.router, { rootURL: '/app/', }); @@ -51,7 +52,7 @@ moduleFor( this.add('template:parent', precompileTemplate('Parent')); this.add('template:dynamic-with-child.child', precompileTemplate('Dynamic Child')); - this.router.reopen({ + classicReopen(this.router, { rootURL: '/app/', }); @@ -138,7 +139,7 @@ moduleFor( } '@test respects the usage of a different rootURL'(assert) { - this.router.reopen({ + classicReopen(this.router, { rootURL: '/app/', }); @@ -156,7 +157,7 @@ moduleFor( } '@test must include rootURL'() { - this.router.reopen({ + classicReopen(this.router, { rootURL: '/app/', }); diff --git a/packages/ember/tests/routing/router_service_test/transitionTo_test.js b/packages/ember/tests/routing/router_service_test/transitionTo_test.js index f9087a41b5d..897429f1c8f 100644 --- a/packages/ember/tests/routing/router_service_test/transitionTo_test.js +++ b/packages/ember/tests/routing/router_service_test/transitionTo_test.js @@ -9,6 +9,7 @@ import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@glimmer/manager'; import { RouterTestCase, moduleFor } from 'internal-test-helpers'; import { InternalTransition as Transition } from 'router_js'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Router Service - transitionTo', @@ -260,7 +261,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: ['sort'], sort: 'ASC', }) @@ -282,7 +283,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: ['sort'], }) ); @@ -309,7 +310,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: ['sort', 'page', 'category', 'extra'], sort: 'ASC', page: null, @@ -335,7 +336,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: { cont_sort: 'url_sort', }, @@ -361,7 +362,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: { cont_sort: 'url_sort', }, @@ -400,7 +401,7 @@ moduleFor( this.add( 'route:parent.child', - Route.extend({ + classicExtend(Route, { queryParams: { cont_sort: { as: 'url_sort' }, }, @@ -430,7 +431,7 @@ moduleFor( ); this.add( 'controller:parent', - Controller.extend({ + classicExtend(Controller, { queryParams: ['url_sort'], }) ); @@ -463,7 +464,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: ['bar'], bar: null, }) @@ -504,7 +505,7 @@ moduleFor( this.add( 'controller:parent.child', - Controller.extend({ + classicExtend(Controller, { queryParams: ['bar'], bar: null, }) @@ -615,7 +616,7 @@ moduleFor( this.add( 'controller:application', - Controller.extend({ + classicExtend(Controller, { queryParams: ['filter', 'sort'], filter: '', sort: '', @@ -673,7 +674,7 @@ moduleFor( this.add( 'controller:application', - Controller.extend({ + classicExtend(Controller, { queryParams: ['filter', 'sort'], filter: '', sort: '', diff --git a/packages/ember/tests/service_injection_test.js b/packages/ember/tests/service_injection_test.js index 1e6033f9e1d..a9635433af8 100644 --- a/packages/ember/tests/service_injection_test.js +++ b/packages/ember/tests/service_injection_test.js @@ -5,6 +5,7 @@ import { _ProxyMixin } from '@ember/-internals/runtime'; import { moduleFor, ApplicationTestCase } from 'internal-test-helpers'; import { computed } from '@ember/object'; import { precompileTemplate } from '@ember/template-compilation'; +import { classicExtend } from '@ember/object/lib/classic'; moduleFor( 'Service Injection', @@ -37,7 +38,7 @@ moduleFor( myService; } ); - let MyService = class extends Service.extend(_ProxyMixin) { + let MyService = class extends classicExtend(Service, _ProxyMixin) { init() { super.init(...arguments); diff --git a/packages/internal-test-helpers/lib/test-cases/application.ts b/packages/internal-test-helpers/lib/test-cases/application.ts index 81a09b77ce0..4dd10474d8d 100644 --- a/packages/internal-test-helpers/lib/test-cases/application.ts +++ b/packages/internal-test-helpers/lib/test-cases/application.ts @@ -7,6 +7,7 @@ import Resolver from '../test-resolver'; import { assert as emberAssert } from '@ember/debug'; import type Controller from '@ember/controller'; import type ApplicationInstance from '@ember/application/instance'; +import { classicExtend } from '@ember/object/lib/classic'; export default abstract class ApplicationTestCase extends TestResolverApplicationTestCase { application: Application; @@ -24,7 +25,7 @@ export default abstract class ApplicationTestCase extends TestResolverApplicatio emberAssert('expected a resolver', resolver instanceof Resolver); this.resolver = resolver; - resolver.add('router:main', Router.extend(this.routerOptions)); + resolver.add('router:main', classicExtend(Router, this.routerOptions)); } createApplication(myOptions = {}, MyApplication = Application) { diff --git a/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts b/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts index eaac5b77e2e..93da72c6cea 100644 --- a/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts +++ b/packages/internal-test-helpers/lib/test-cases/autoboot-application.ts @@ -4,6 +4,7 @@ import Router from '@ember/routing/router'; import Resolver from '../test-resolver'; import { assert } from '@ember/debug'; import type ApplicationInstance from '@ember/application/instance'; +import { classicExtend } from '@ember/object/lib/classic'; export default abstract class AutobootApplicationTestCase extends TestResolverApplicationTestCase { resolver?: Resolver; @@ -18,7 +19,7 @@ export default abstract class AutobootApplicationTestCase extends TestResolverAp assert('expected a resolver', resolver instanceof Resolver); this.resolver = resolver; - resolver.add('router:main', Router.extend(this.routerOptions)); + resolver.add('router:main', classicExtend(Router, this.routerOptions)); return application; } diff --git a/packages/internal-test-helpers/lib/test-cases/query-param.ts b/packages/internal-test-helpers/lib/test-cases/query-param.ts index 18cddb58c0a..432e1aad6d7 100644 --- a/packages/internal-test-helpers/lib/test-cases/query-param.ts +++ b/packages/internal-test-helpers/lib/test-cases/query-param.ts @@ -5,6 +5,7 @@ import NoneLocation from '@ember/routing/none-location'; import ApplicationTestCase from './application'; import { runLoopSettled } from '../run'; +import { classicExtend } from '@ember/object/lib/classic'; export default abstract class QueryParamTestCase extends ApplicationTestCase { expectedPushURL: unknown; @@ -99,7 +100,8 @@ export default abstract class QueryParamTestCase extends ApplicationTestCase { setSingleQPController(routeName: string, param = 'foo', defaultValue = 'bar', options = {}) { this.add( `controller:${routeName}`, - Controller.extend( + classicExtend( + Controller, { queryParams: [param], [param]: defaultValue, @@ -124,7 +126,8 @@ export default abstract class QueryParamTestCase extends ApplicationTestCase { ) { this.add( `controller:${routeName}`, - Controller.extend( + classicExtend( + Controller, { queryParams: { [prop]: urlKey, diff --git a/packages/internal-test-helpers/lib/test-cases/rendering.ts b/packages/internal-test-helpers/lib/test-cases/rendering.ts index 1dab130a719..8c9f8b589c2 100644 --- a/packages/internal-test-helpers/lib/test-cases/rendering.ts +++ b/packages/internal-test-helpers/lib/test-cases/rendering.ts @@ -14,6 +14,7 @@ import buildOwner from '../build-owner'; import { define } from '../module-for'; import { runAppend, runDestroy, runTask } from '../run'; import AbstractTestCase from './abstract'; +import { classicExtend } from '@ember/object/lib/classic'; const TextNode = window.Text; @@ -111,7 +112,7 @@ export default abstract class RenderingTestCase extends AbstractTestCase { layoutName: '-top-level', }); - owner.register('component:-top-level', Component.extend(attrs)); + owner.register('component:-top-level', classicExtend(Component, attrs)); this.component = owner.lookup('component:-top-level'); @@ -137,7 +138,7 @@ export default abstract class RenderingTestCase extends AbstractTestCase { if (typeof funcOrClassBody === 'function') { this.owner.register(`helper:${name}`, helper(funcOrClassBody)); } else if (typeof funcOrClassBody === 'object' && funcOrClassBody !== null) { - this.owner.register(`helper:${name}`, Helper.extend(funcOrClassBody)); + this.owner.register(`helper:${name}`, classicExtend(Helper, funcOrClassBody)); } else { throw new Error(`Cannot register ${funcOrClassBody} as a helper`); } diff --git a/packages/internal-test-helpers/lib/test-cases/router-non-application.ts b/packages/internal-test-helpers/lib/test-cases/router-non-application.ts index 16e882935c7..755e660f2a0 100644 --- a/packages/internal-test-helpers/lib/test-cases/router-non-application.ts +++ b/packages/internal-test-helpers/lib/test-cases/router-non-application.ts @@ -13,6 +13,7 @@ import { runAppend, runDestroy } from '../run'; import type { BootOptions, EngineInstanceOptions } from '@ember/engine/instance'; import type EngineInstance from '@ember/engine/instance'; import type { InternalFactory } from '@ember/-internals/owner'; +import { classicExtend } from '@ember/object/lib/classic'; export default class RouterNonApplicationTestCase extends AbstractTestCase { owner: EngineInstance; @@ -139,7 +140,7 @@ export default class RouterNonApplicationTestCase extends AbstractTestCase { layoutName: '-top-level', }); - owner.register('component:-top-level', Component.extend(attrs)); + owner.register('component:-top-level', classicExtend(Component, attrs)); this.component = owner.lookup('component:-top-level'); From b44875e321bedc3ebdc09812825d92ffdb8c30a7 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:46:59 -0400 Subject: [PATCH 2/2] New temporary addition to the package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 52ed8c88fb0..a643e095769 100644 --- a/package.json +++ b/package.json @@ -271,6 +271,7 @@ "@ember/object/events.js": "ember-source/@ember/object/events.js", "@ember/object/index.js": "ember-source/@ember/object/index.js", "@ember/object/internals.js": "ember-source/@ember/object/internals.js", + "@ember/object/lib/classic.js": "ember-source/@ember/object/lib/classic.js", "@ember/object/lib/computed/computed_macros.js": "ember-source/@ember/object/lib/computed/computed_macros.js", "@ember/object/lib/computed/reduce_computed_macros.js": "ember-source/@ember/object/lib/computed/reduce_computed_macros.js", "@ember/object/mixin.js": "ember-source/@ember/object/mixin.js",