From eabcefb04d07ad8a8f14b70d9abad01e26f54591 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:30:06 -0400 Subject: [PATCH] Add INTERNAL_MIXIN_EXTEND for internal extend with deprecated mixins The public `extend` notifies when a registered deprecated mixin is applied. EmberObject must still extend with `Observable`, so it needs a silent path. The Symbol-keyed method mirrors `INTERNAL_MIXIN_CREATE`: the public method is the notice plus the internal call. This replaces the `static {}` block that applied `Observable` through `PrototypeMixin.reopen`. EmberObject returns to the shape it had on main: an anonymous intermediate class holds the mixin. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016GSgBwzsjV3iTreDTN3fHZ --- .../-internals/utils/lib/internal-mixin-create.ts | 11 +++++++++++ packages/@ember/object/core.ts | 14 +++++++++++++- packages/@ember/object/index.ts | 9 +++------ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/@ember/-internals/utils/lib/internal-mixin-create.ts b/packages/@ember/-internals/utils/lib/internal-mixin-create.ts index c01119d15aa..b883dbcffc0 100644 --- a/packages/@ember/-internals/utils/lib/internal-mixin-create.ts +++ b/packages/@ember/-internals/utils/lib/internal-mixin-create.ts @@ -9,3 +9,14 @@ @private */ export const INTERNAL_MIXIN_CREATE = Symbol('__internal__mixin__'); + +/** + Key for the internal equivalent of `CoreObject.extend`. + + The public `extend` notifies when a deprecated framework mixin such as + `Observable` is applied. Ember's own classes must still extend with those + mixins, so they use this Symbol-keyed method, which skips the notice. + + @private +*/ +export const INTERNAL_MIXIN_EXTEND = Symbol('__internal__mixin__extend__'); diff --git a/packages/@ember/object/core.ts b/packages/@ember/object/core.ts index c9ad9281aa2..5132a9066bc 100644 --- a/packages/@ember/object/core.ts +++ b/packages/@ember/object/core.ts @@ -15,7 +15,10 @@ 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 { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixin-create'; +import { + INTERNAL_MIXIN_CREATE, + INTERNAL_MIXIN_EXTEND, +} from '@ember/-internals/utils/lib/internal-mixin-create'; import { deprecateAppliedMixins } from '@ember/-internals/utils/lib/deprecated-mixin'; import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler'; import makeArray from '@ember/array/make'; @@ -716,6 +719,15 @@ class CoreObject { ): Readonly & EmberClassConstructor & MergeArray; static extend(...mixins: any[]) { deprecateAppliedMixins(mixins); + return this[INTERNAL_MIXIN_EXTEND](...mixins); + } + + /** @internal */ + static [INTERNAL_MIXIN_EXTEND]>( + this: Statics & EmberClassConstructor, + ...mixins: M + ): Readonly & EmberClassConstructor & MergeArray; + static [INTERNAL_MIXIN_EXTEND](...mixins: any[]) { let Class = class extends this {}; reopen.apply(Class.PrototypeMixin, mixins); return Class; diff --git a/packages/@ember/object/index.ts b/packages/@ember/object/index.ts index 39b16e43620..eaffcc7d446 100644 --- a/packages/@ember/object/index.ts +++ b/packages/@ember/object/index.ts @@ -8,6 +8,7 @@ import { isElementDescriptor, setClassicDecorator } from '@ember/-internals/meta import expandProperties from '@ember/-internals/metal/lib/expand_properties'; import { getFactoryFor } from '@ember/-internals/container/lib/container'; import { setObservers } from '@ember/-internals/utils/lib/super'; +import { INTERNAL_MIXIN_EXTEND } from '@ember/-internals/utils/lib/internal-mixin-create'; import type { AnyFn } from '@ember/-internals/utility-types'; import CoreObject from '@ember/object/core'; import Observable from '@ember/object/observable'; @@ -36,12 +37,8 @@ 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 { - static { - // `extend(Observable)` would fire the Observable deprecation for every app. - this.PrototypeMixin.reopen(Observable); - } - +// `extend(Observable)` would fire the Observable deprecation for every app. +class EmberObject extends CoreObject[INTERNAL_MIXIN_EXTEND](Observable) { get _debugContainerKey() { let factory = getFactoryFor(this); return factory !== undefined && factory.fullName;