diff --git a/index.html b/index.html
index d67e07a7834..2c31a761e38 100644
--- a/index.html
+++ b/index.html
@@ -33,6 +33,10 @@
EmberENV['_ALL_DEPRECATIONS_ENABLED'] = true;
}
+ if (QUnit.urlParams.NO_EMBER_OBJECT) {
+ EmberENV['_NO_EMBER_OBJECT'] = true;
+ }
+
if (QUnit.urlParams.OVERRIDE_DEPRECATION_VERSION) {
EmberENV['_OVERRIDE_DEPRECATION_VERSION'] = QUnit.urlParams.OVERRIDE_DEPRECATION_VERSION;
}
diff --git a/packages/@ember/-internals/deprecations/index.ts b/packages/@ember/-internals/deprecations/index.ts
index b8a83a7054d..f28aad7de22 100644
--- a/packages/@ember/-internals/deprecations/index.ts
+++ b/packages/@ember/-internals/deprecations/index.ts
@@ -168,6 +168,13 @@ export const DEPRECATIONS = {
until: '8.0.0',
url: 'https://deprecations.emberjs.com/id/deprecate-ember-utils',
}),
+ DEPRECATE_EMBER_OBJECT: deprecation({
+ id: 'deprecate-ember-object',
+ for: 'ember-source',
+ since: { available: '7.5.0' },
+ until: '9.0.0',
+ url: 'https://deprecations.emberjs.com/id/deprecate-ember-object',
+ }),
};
export function deprecateUntil(message: string, deprecation: DeprecationObject) {
@@ -183,3 +190,30 @@ export function deprecateUntil(message: string, deprecation: DeprecationObject)
}
deprecate(message, deprecation.test, options);
}
+
+declare global {
+ interface ImportMetaEnv {
+ EMBER_DROP_CLASSIC_FEATURES?: boolean | string;
+ }
+}
+
+/**
+ Reports the use of an API that RFC 1234 removes with `EmberObject`.
+
+ The `EMBER_DROP_CLASSIC_FEATURES` build flag is a preview of the removal:
+ when it is set, the API throws.
+
+ @private
+*/
+export function deprecateEmberObject(api: string) {
+ if (import.meta.env?.EMBER_DROP_CLASSIC_FEATURES) {
+ throw new Error(
+ `${api} is not available, because the \`EMBER_DROP_CLASSIC_FEATURES\` build flag is set. Please see ${DEPRECATIONS.DEPRECATE_EMBER_OBJECT.options.url} for more details.`
+ );
+ }
+
+ deprecateUntil(
+ `${api} is deprecated. Use native classes with tracked properties instead.`,
+ DEPRECATIONS.DEPRECATE_EMBER_OBJECT
+ );
+}
diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts
index c7e1027ca35..9eebe5a9212 100644
--- a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts
+++ b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts
@@ -5,6 +5,7 @@ import {
setOwner,
} from '@ember/-internals/owner';
import { guidFor } from '@ember/-internals/utils/lib/guid';
+import setProperties from '@ember/-internals/metal/lib/set_properties';
import {
addChildView,
setElementView,
@@ -454,7 +455,7 @@ export default class CurlyComponentManager
bucket.argsRevision = valueForTag(argsTag);
component[IS_DISPATCHING_ATTRS] = true;
- component.setProperties(props);
+ setProperties(component, props);
component[IS_DISPATCHING_ATTRS] = false;
sendCoreViewEvent(component, 'didUpdateAttrs');
diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts b/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts
index 538377dcea2..9ad7bb7b06a 100644
--- a/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts
+++ b/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts
@@ -1,6 +1,7 @@
import type { InternalOwner } from '@ember/-internals/owner';
import { generateControllerFactory } from '@ember/routing/lib/generate_controller';
import { assert } from '@ember/debug';
+import { set } from '@ember/-internals/metal/lib/property_set';
import type EngineInstance from '@ember/engine/instance';
import { associateDestroyableChild } from '@glimmer/destroyable';
import type {
@@ -161,7 +162,7 @@ class MountManager
let { controller, modelRef } = bucket;
if (modelRef !== undefined) {
- controller.set('model', valueForRef(modelRef));
+ set(controller, 'model', valueForRef(modelRef));
}
}
}
diff --git a/packages/@ember/-internals/glimmer/lib/component.ts b/packages/@ember/-internals/glimmer/lib/component.ts
index 5c0735cd780..7f9883987b7 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 { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
// Keep track of which component classes have already been processed for lazy event setup.
let lazyEventsProcessed = new WeakMap>();
@@ -1051,7 +1052,7 @@ class Component
getAttr(key: string) {
// TODO Intimate API should be deprecated
- return this.get(key);
+ return get(this, key);
}
/**
@@ -1693,4 +1694,6 @@ Component.reopenClass({
setInternalComponentManager(CURLY_COMPONENT_MANAGER, Component);
+setFrameworkClass(Component);
+
export default Component;
diff --git a/packages/@ember/-internals/glimmer/lib/helper.ts b/packages/@ember/-internals/glimmer/lib/helper.ts
index 81e59cca7e0..571e86dfe09 100644
--- a/packages/@ember/-internals/glimmer/lib/helper.ts
+++ b/packages/@ember/-internals/glimmer/lib/helper.ts
@@ -16,6 +16,7 @@ import type { DirtyableTag } from '@glimmer/interfaces';
import { consumeTag } from '@glimmer/validator/lib/tracking';
import { createTag, DIRTY_TAG as dirtyTag } from '@glimmer/validator/lib/validators';
import { IS_CLASSIC_HELPER } from './helper-brand';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
export { isClassicHelper } from './helper-brand';
@@ -408,3 +409,5 @@ export function helper(
// `helper()` invocation in favor of using plain functions.
return new Wrapper(helperFn) as unknown as FunctionBasedHelper;
}
+
+setFrameworkClass(Helper);
diff --git a/packages/@ember/-internals/runtime/lib/mixins/-proxy.ts b/packages/@ember/-internals/runtime/lib/mixins/-proxy.ts
index cf3e23234e6..ca147af0651 100644
--- a/packages/@ember/-internals/runtime/lib/mixins/-proxy.ts
+++ b/packages/@ember/-internals/runtime/lib/mixins/-proxy.ts
@@ -110,7 +110,7 @@ const ProxyMixin = /*@__PURE__*/ Mixin[INTERNAL_MIXIN_CREATE]({
},
willDestroy() {
- this.set('content', null);
+ set(this, 'content', null);
this._super(...arguments);
},
diff --git a/packages/@ember/-internals/utils/lib/internal-framework-class.ts b/packages/@ember/-internals/utils/lib/internal-framework-class.ts
new file mode 100644
index 00000000000..6b04e703163
--- /dev/null
+++ b/packages/@ember/-internals/utils/lib/internal-framework-class.ts
@@ -0,0 +1,31 @@
+/**
+ Marks the classes that Ember itself builds on `EmberObject`, such as
+ `Route`, `Controller` and `Service`.
+
+ The `EmberObject` deprecation applies to classes that extend `EmberObject`
+ directly. A class that has this mark, and each subclass of it, stays
+ usable.
+
+ This is a Symbol so that application code cannot set the mark by name.
+
+ @private
+*/
+const FRAMEWORK_CLASS = Symbol('__internal__framework__class__');
+
+export function setFrameworkClass(Class: object): void {
+ (Class as Record)[FRAMEWORK_CLASS] = true;
+}
+
+/**
+ True for a marked class and for each subclass of it.
+*/
+export function isFrameworkClass(Class: object): boolean {
+ return (Class as Record)[FRAMEWORK_CLASS] === true;
+}
+
+/**
+ True only for the class that Ember marked, not for its subclasses.
+*/
+export function isOwnFrameworkClass(Class: object): boolean {
+ return Object.prototype.hasOwnProperty.call(Class, FRAMEWORK_CLASS);
+}
diff --git a/packages/@ember/-internals/views/lib/system/event_dispatcher.ts b/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
index 1c1357cc3bd..62312ec34f0 100644
--- a/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
+++ b/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
@@ -6,6 +6,7 @@ import EmberObject from '@ember/object';
import { getElementView } from './utils';
import type { BootEnvironment } from '@ember/engine/instance';
import type Component from '@ember/-internals/glimmer/lib/component';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
@module ember
@@ -316,3 +317,5 @@ export default class EventDispatcher extends EmberObject {
return '(EventDispatcher)';
}
}
+
+setFrameworkClass(EventDispatcher);
diff --git a/packages/@ember/-internals/views/lib/views/core_view.ts b/packages/@ember/-internals/views/lib/views/core_view.ts
index c1a7d1c0a15..f8df30d3208 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 { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
`CoreView` is an abstract class that exists to give view-like behavior
@@ -135,4 +136,6 @@ class CoreView extends FrameworkObject.extend(ActionHandler) {
// Declare on the prototype to have a single shared value.
CoreView.prototype._states = states;
+setFrameworkClass(CoreView);
+
export default CoreView;
diff --git a/packages/@ember/application/index.ts b/packages/@ember/application/index.ts
index e4ac76f8de3..93240716b3e 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 { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
* @deprecated Use `import { getOwner } from '@ember/owner';` instead.
@@ -1133,4 +1134,6 @@ function commonSetupRegistry(registry: Registry) {
registry.register('service:router', RouterService);
}
+setFrameworkClass(Application);
+
export { Application as default };
diff --git a/packages/@ember/application/instance.ts b/packages/@ember/application/instance.ts
index f006714131c..17335100204 100644
--- a/packages/@ember/application/instance.ts
+++ b/packages/@ember/application/instance.ts
@@ -20,6 +20,7 @@ import Router from '@ember/routing/router';
import EventDispatcher from '@ember/-internals/views/lib/system/event_dispatcher';
import type Registry from '@ember/-internals/container/lib/registry';
import type { SimpleElement } from '@simple-dom/interface';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
The `ApplicationInstance` encapsulates all of the stateful aspects of a
@@ -503,4 +504,6 @@ class _BootOptions {
}
}
+setFrameworkClass(ApplicationInstance);
+
export default ApplicationInstance;
diff --git a/packages/@ember/application/namespace.ts b/packages/@ember/application/namespace.ts
index 6b3277f24be..b1e7b636bab 100644
--- a/packages/@ember/application/namespace.ts
+++ b/packages/@ember/application/namespace.ts
@@ -18,6 +18,7 @@ import { getName, setName } from '@ember/-internals/utils/lib/name';
import { guidFor } from '@ember/-internals/utils/lib/guid';
import { assert } from '@ember/debug';
import EmberObject from '@ember/object';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
A Namespace is an object usually used to contain other objects or methods
@@ -78,4 +79,6 @@ class Namespace extends EmberObject {
// Declare on the prototype to have a single shared value.
Namespace.prototype.isNamespace = true;
+setFrameworkClass(Namespace);
+
export default Namespace;
diff --git a/packages/@ember/array/proxy.ts b/packages/@ember/array/proxy.ts
index 192c55215ee..de7f2937663 100644
--- a/packages/@ember/array/proxy.ts
+++ b/packages/@ember/array/proxy.ts
@@ -29,6 +29,7 @@ import {
import { consumeTag } from '@glimmer/validator/lib/tracking';
import { tagFor } from '@glimmer/validator/lib/meta';
import type { Tag } from '@glimmer/interfaces';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
function isMutable(obj: T[] | EmberArray): obj is T[] | MutableArray {
return Array.isArray(obj) || typeof (obj as MutableArray).replace === 'function';
@@ -387,7 +388,7 @@ class ArrayProxy extends EmberObject implements PropertyDidChange {
this._arrangedContentTag === null ||
!validateTag(this._arrangedContentTag, this._arrangedContentRevision!)
) {
- let arrangedContent = this.get('arrangedContent');
+ let arrangedContent = get(this, 'arrangedContent');
if (this._arrangedContentTag === null) {
// This is the first time the proxy has been setup, only add the observer
@@ -416,4 +417,6 @@ ArrayProxy.reopen(MutableArray, {
arrangedContent: alias('content'),
});
+setFrameworkClass(ArrayProxy);
+
export default ArrayProxy;
diff --git a/packages/@ember/controller/index.ts b/packages/@ember/controller/index.ts
index b83b4e6ae98..d195b4bdb2a 100644
--- a/packages/@ember/controller/index.ts
+++ b/packages/@ember/controller/index.ts
@@ -13,6 +13,7 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi
import type { RouteArgs } from '@ember/routing/-internals';
import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler';
import type { Transition } from 'router_js';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
export type ControllerQueryParamType = 'boolean' | 'number' | 'array' | 'string';
export type ControllerQueryParam =
@@ -375,6 +376,8 @@ export function inject(
return metalInject('controller', ...args);
}
+setFrameworkClass(Controller);
+
export { Controller as default, ControllerMixin };
/**
diff --git a/packages/@ember/debug/container-debug-adapter.ts b/packages/@ember/debug/container-debug-adapter.ts
index dec462e13a0..6831bdf9b5d 100644
--- a/packages/@ember/debug/container-debug-adapter.ts
+++ b/packages/@ember/debug/container-debug-adapter.ts
@@ -5,6 +5,7 @@ import type Owner from '@ember/owner';
import { getOwner } from '@ember/-internals/owner';
import type { Resolver } from '@ember/-internals/owner';
import Namespace from '@ember/application/namespace';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
@module @ember/debug/container-debug-adapter
@@ -107,3 +108,5 @@ export default class ContainerDebugAdapter extends EmberObject {
return types;
}
}
+
+setFrameworkClass(ContainerDebugAdapter);
diff --git a/packages/@ember/debug/data-adapter.ts b/packages/@ember/debug/data-adapter.ts
index 4b37faaff4b..13f6abbffea 100644
--- a/packages/@ember/debug/data-adapter.ts
+++ b/packages/@ember/debug/data-adapter.ts
@@ -12,6 +12,7 @@ import { consumeTag, createCache, getValue, untrack } from '@glimmer/validator/l
import { tagFor } from '@glimmer/validator/lib/meta';
import type ContainerDebugAdapter from '@ember/debug/container-debug-adapter';
import { assert } from '.';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
/**
@module @ember/debug/data-adapter
@@ -685,3 +686,5 @@ export default class DataAdapter extends EmberObject {
return null;
}
}
+
+setFrameworkClass(DataAdapter);
diff --git a/packages/@ember/engine/index.ts b/packages/@ember/engine/index.ts
index 478de9e9b13..de210c4fc7f 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 { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
function props(obj: object) {
let properties = [];
@@ -532,4 +533,6 @@ function commonSetupRegistry(registry: Registry) {
registry.register('container-debug-adapter:main', ContainerDebugAdapter);
}
+setFrameworkClass(Engine);
+
export default Engine;
diff --git a/packages/@ember/engine/instance.ts b/packages/@ember/engine/instance.ts
index b49d1280121..34d62207c1a 100644
--- a/packages/@ember/engine/instance.ts
+++ b/packages/@ember/engine/instance.ts
@@ -16,6 +16,7 @@ import { type FullName, isFactory } from '@ember/-internals/owner';
import type Engine from '@ember/engine';
import type Application from '@ember/application';
import type { SimpleElement } from '@simple-dom/interface';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
export interface BootOptions {
isBrowser?: boolean;
@@ -265,4 +266,6 @@ class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerPro
}
}
+setFrameworkClass(EngineInstance);
+
export default EngineInstance;
diff --git a/packages/@ember/object/-internals.ts b/packages/@ember/object/-internals.ts
index 9021a8dd03d..5d50e471ad3 100644
--- a/packages/@ember/object/-internals.ts
+++ b/packages/@ember/object/-internals.ts
@@ -5,6 +5,7 @@ import { addListener } from '@ember/-internals/metal/lib/events';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import EmberObject from '.';
+import { setFrameworkClass } from '@ember/-internals/utils/lib/internal-framework-class';
// Here we have runtime shenanigans to add debug-only errors to the class in dev
// builds. Those runtime shenanigans produce the need for type-level shenanigans
@@ -47,4 +48,6 @@ if (DEBUG) {
addListener(FrameworkObject.prototype, 'init', null, ASSERT_INIT_WAS_CALLED);
}
+setFrameworkClass(FrameworkObject);
+
export { FrameworkObject };
diff --git a/packages/@ember/object/computed.ts b/packages/@ember/object/computed.ts
index 5e4d41bc33b..aa6ba2a3f59 100644
--- a/packages/@ember/object/computed.ts
+++ b/packages/@ember/object/computed.ts
@@ -1,40 +1,54 @@
+import { deprecateEmberObject } from '@ember/-internals/deprecations';
+import type { AnyFn } from '@ember/-internals/utility-types';
+import internalAlias from '@ember/-internals/metal/lib/alias';
+import * as macros from './lib/computed/computed_macros';
+import * as reduceMacros from './lib/computed/reduce_computed_macros';
+
export { ComputedProperty as default } from '@ember/-internals/metal/lib/computed';
export { default as expandProperties } from '@ember/-internals/metal/lib/expand_properties';
-export { default as alias } from '@ember/-internals/metal/lib/alias';
-export {
- empty,
- notEmpty,
- none,
- not,
- bool,
- match,
- equal,
- gt,
- gte,
- lt,
- lte,
- oneWay,
- oneWay as reads,
- readOnly,
- deprecatingAlias,
- and,
- or,
-} from './lib/computed/computed_macros';
+/**
+ Makes the public version of a macro. Ember's own code imports the macros
+ from `./lib/computed`, which do not report the deprecation.
+*/
+function deprecated(name: string, macro: T): T {
+ return function (this: unknown, ...args: unknown[]) {
+ deprecateEmberObject(`The \`${name}\` macro from \`@ember/object/computed\``);
+ return macro.apply(this, args);
+ } as T;
+}
+
+export const alias = deprecated('alias', internalAlias);
+
+export const empty = deprecated('empty', macros.empty);
+export const notEmpty = deprecated('notEmpty', macros.notEmpty);
+export const none = deprecated('none', macros.none);
+export const not = deprecated('not', macros.not);
+export const bool = deprecated('bool', macros.bool);
+export const match = deprecated('match', macros.match);
+export const equal = deprecated('equal', macros.equal);
+export const gt = deprecated('gt', macros.gt);
+export const gte = deprecated('gte', macros.gte);
+export const lt = deprecated('lt', macros.lt);
+export const lte = deprecated('lte', macros.lte);
+export const oneWay = deprecated('oneWay', macros.oneWay);
+export const readOnly = deprecated('readOnly', macros.readOnly);
+export const deprecatingAlias = deprecated('deprecatingAlias', macros.deprecatingAlias);
+export const and = deprecated('and', macros.and);
+export const or = deprecated('or', macros.or);
+export const reads = deprecated('reads', macros.oneWay);
-export {
- sum,
- min,
- max,
- map,
- sort,
- setDiff,
- mapBy,
- filter,
- filterBy,
- uniq,
- uniqBy,
- union,
- intersect,
- collect,
-} from './lib/computed/reduce_computed_macros';
+export const sum = deprecated('sum', reduceMacros.sum);
+export const min = deprecated('min', reduceMacros.min);
+export const max = deprecated('max', reduceMacros.max);
+export const map = deprecated('map', reduceMacros.map);
+export const sort = deprecated('sort', reduceMacros.sort);
+export const setDiff = deprecated('setDiff', reduceMacros.setDiff);
+export const mapBy = deprecated('mapBy', reduceMacros.mapBy);
+export const filter = deprecated('filter', reduceMacros.filter);
+export const filterBy = deprecated('filterBy', reduceMacros.filterBy);
+export const uniq = deprecated('uniq', reduceMacros.uniq);
+export const uniqBy = deprecated('uniqBy', reduceMacros.uniqBy);
+export const union = deprecated('union', reduceMacros.union);
+export const intersect = deprecated('intersect', reduceMacros.intersect);
+export const collect = deprecated('collect', reduceMacros.collect);
diff --git a/packages/@ember/object/core.ts b/packages/@ember/object/core.ts
index 2dcec80ec1c..3b21efbeb82 100644
--- a/packages/@ember/object/core.ts
+++ b/packages/@ember/object/core.ts
@@ -20,6 +20,11 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi
import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler';
import makeArray from '@ember/array/make';
import { assert } from '@ember/debug';
+import { deprecateEmberObject } from '@ember/-internals/deprecations';
+import {
+ isFrameworkClass,
+ isOwnFrameworkClass,
+} from '@ember/-internals/utils/lib/internal-framework-class';
import { DEBUG } from '@glimmer/env';
import { destroy, isDestroying, isDestroyed, registerDestructor } from '@glimmer/destroyable';
import { OWNER } from '@glimmer/owner';
@@ -56,6 +61,35 @@ function hasToStringExtension(val: unknown): val is HasToStringExtension {
const reopen = Mixin.prototype.reopen;
const wasApplied = new WeakSet();
+const INIT_OVERRIDES = new WeakMap