Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/@ember/-internals/deprecations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ export const DEPRECATIONS = {
until: '8.0.0',
url: 'https://deprecations.emberjs.com/id/deprecate-mixins',
}),
DEPRECATE_PROMISE_PROXY_MIXIN: deprecation({
id: 'deprecate-promise-proxy-mixin',
for: 'ember-source',
since: { available: '7.4.0' },
until: '8.0.0',
url: 'https://deprecations.emberjs.com/id/deprecate-promise-proxy-mixin',
}),
DEPRECATE_ENUMERABLE: deprecation({
id: 'deprecate-enumerable',
for: 'ember-source',
since: { available: '7.4.0' },
until: '8.0.0',
url: 'https://deprecations.emberjs.com/id/deprecate-enumerable',
}),
DEPRECATE_OBSERVABLE: deprecation({
id: 'deprecate-observable',
for: 'ember-source',
since: { available: '7.4.0' },
until: '8.0.0',
url: 'https://deprecations.emberjs.com/id/deprecate-observable',
}),
};

export function deprecateUntil(message: string, deprecation: DeprecationObject) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import { computed, get, set } from '@ember/object';
import { Promise } from 'rsvp';
import { fn } from '@ember/helper';
import { on } from '@ember/modifier';
import { moduleFor, RenderingTestCase, strip, runTask } from 'internal-test-helpers';
import {
moduleFor,
RenderingTestCase,
strip,
runTask,
expectDeprecation,
testUnless,
} from 'internal-test-helpers';
import { DEPRECATIONS } from '@ember/-internals/deprecations';
import GlimmerishComponent from '../../utils/glimmerish-component';
import Component from '@glimmer/component';
import { Component as EmberComponent } from '../../utils/helpers';
Expand Down Expand Up @@ -96,8 +104,17 @@ moduleFor(
this.assertText('max jackson | max jackson');
}

'@test creating an array proxy inside a tracking context does not trigger backtracking assertion'() {
let PromiseArray = ArrayProxy.extend(PromiseProxyMixin);
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test creating an array proxy inside a tracking context does not trigger backtracking assertion`]() {
let PromiseArray;
expectDeprecation(
() => {
PromiseArray = ArrayProxy.extend(PromiseProxyMixin);
},
/The `PromiseProxyMixin` is deprecated/,
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isEnabled
);

class LoaderComponent extends GlimmerishComponent {
get data() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
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 { moduleFor, AbstractTestCase, ignoreDeprecation, testUnless } from 'internal-test-helpers';
import { DEPRECATIONS } from '../../../deprecations';

let ObjectPromiseProxy, proxy;

moduleFor(
'Ember.PromiseProxy - ObjectProxy',
class extends AbstractTestCase {
beforeEach() {
ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);
// The notice for applying `PromiseProxyMixin` has its own test. These
// tests count their own assertions, so the notice must stay silent here.
ignoreDeprecation(() => {
ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);
});
}

afterEach() {
Expand All @@ -22,11 +27,15 @@ moduleFor(
proxy = undefined;
}

['@test present on ember namespace'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test present on ember namespace`](assert) {
assert.ok(PromiseProxyMixin, 'expected PromiseProxyMixin to exist');
}

['@test no promise, invoking then should raise'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test no promise, invoking then should raise`](assert) {
proxy = ObjectPromiseProxy.create();

assert.throws(function () {
Expand All @@ -41,7 +50,9 @@ moduleFor(
}, new RegExp("PromiseProxy's promise must be set"));
}

['@test fulfillment'](assert) {
[`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test fulfillment`](
assert
) {
let value = {
firstName: 'stef',
lastName: 'penner',
Expand Down Expand Up @@ -155,7 +166,9 @@ moduleFor(
// rest of the promise semantics are tested in directly in RSVP
}

['@test rejection'](assert) {
[`${testUnless(DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved)} @test rejection`](
assert
) {
let reason = new Error('failure');
let deferred = RSVP.defer();
proxy = ObjectPromiseProxy.create({
Expand Down Expand Up @@ -259,7 +272,9 @@ moduleFor(
}

// https://github.com/emberjs/ember.js/issues/15694
['@test rejection without specifying reason'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test rejection without specifying reason`](assert) {
let deferred = RSVP.defer();
proxy = ObjectPromiseProxy.create({
promise: deferred.promise,
Expand Down Expand Up @@ -328,7 +343,9 @@ moduleFor(
);
}

["@test unhandled rejects still propagate to RSVP.on('error', ...) "](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test unhandled rejects still propagate to RSVP.on('error', ...) `](assert) {
assert.expect(1);

RSVP.on('error', onerror);
Expand Down Expand Up @@ -361,7 +378,9 @@ moduleFor(
RSVP.off('error', onerror);
}

['@test should work with promise inheritance'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should work with promise inheritance`](assert) {
class PromiseSubclass extends RSVP.Promise {}

proxy = ObjectPromiseProxy.create({
Expand All @@ -371,7 +390,9 @@ moduleFor(
assert.ok(proxy.then() instanceof PromiseSubclass, 'promise proxy respected inheritance');
}

['@test should reset isFulfilled and isRejected when promise is reset'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should reset isFulfilled and isRejected when promise is reset`](assert) {
let deferred = EmberRSVP.defer();

proxy = ObjectPromiseProxy.create({
Expand Down Expand Up @@ -470,7 +491,9 @@ moduleFor(
);
}

['@test should have content when isFulfilled is set'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should have content when isFulfilled is set`](assert) {
let deferred = EmberRSVP.defer();

proxy = ObjectPromiseProxy.create({
Expand All @@ -482,7 +505,9 @@ moduleFor(
run(deferred, 'resolve', true);
}

['@test should have reason when isRejected is set'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should have reason when isRejected is set`](assert) {
let error = new Error('Y U REJECT?!?');
let deferred = EmberRSVP.defer();

Expand All @@ -499,7 +524,9 @@ moduleFor(
}
}

['@test should not error if promise is resolved after proxy has been destroyed'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should not error if promise is resolved after proxy has been destroyed`](assert) {
let deferred = EmberRSVP.defer();

proxy = ObjectPromiseProxy.create({
Expand All @@ -521,7 +548,9 @@ moduleFor(
);
}

['@test should not error if promise is rejected after proxy has been destroyed'](assert) {
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test should not error if promise is rejected after proxy has been destroyed`](assert) {
let deferred = EmberRSVP.defer();

proxy = ObjectPromiseProxy.create({
Expand All @@ -543,7 +572,9 @@ moduleFor(
);
}

['@test promise chain is not broken if promised is resolved after proxy has been destroyed'](
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test promise chain is not broken if promised is resolved after proxy has been destroyed`](
assert
) {
let deferred = EmberRSVP.defer();
Expand Down Expand Up @@ -575,7 +606,9 @@ moduleFor(
);
}

['@test promise chain is not broken if promised is rejected after proxy has been destroyed'](
[`${testUnless(
DEPRECATIONS.DEPRECATE_PROMISE_PROXY_MIXIN.isRemoved
)} @test promise chain is not broken if promised is rejected after proxy has been destroyed`](
assert
) {
let deferred = EmberRSVP.defer();
Expand Down
44 changes: 44 additions & 0 deletions packages/@ember/-internals/utils/lib/deprecated-mixin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
Registry of the deprecation notice that belongs to a single framework mixin.

`Mixin.create` deprecates the act of authoring a mixin. A framework mixin
such as `Observable` is built through `INTERNAL_MIXIN_CREATE`, so it stays
silent while Ember applies it. Application code that applies the same mixin
must still get a notice for that specific mixin, so the public entry points
(`Mixin.create`, `CoreObject.extend`, `CoreObject.reopen`) look the mixin up
here and call its notice.

The registry is a `WeakMap`, so the association is not visible on the mixin.

@private
*/
const NOTICES = new WeakMap<object, () => void>();

/**
Records the deprecation notice for a framework mixin and returns the mixin.

@private
*/
export function deprecatedMixin<T extends object>(mixin: T, notice: () => void): T {
NOTICES.set(mixin, notice);
return mixin;
}

/**
Calls the deprecation notice of every value that is a deprecated framework
mixin. Other values are ignored.

@private
*/
export function deprecateAppliedMixins(mixins: ArrayLike<unknown>): void {
for (let i = 0; i < mixins.length; i++) {
let mixin = mixins[i];
if (typeof mixin !== 'object' || mixin === null) {
continue;
}
let notice = NOTICES.get(mixin);
if (notice !== undefined) {
notice();
}
}
}
10 changes: 9 additions & 1 deletion packages/@ember/enumerable/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Mixin from '@ember/object/mixin';
import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixin-create';
import { deprecatedMixin } from '@ember/-internals/utils/lib/deprecated-mixin';
import { deprecateUntil, DEPRECATIONS } from '@ember/-internals/deprecations';

/**
@module @ember/enumerable
Expand All @@ -13,9 +15,15 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi

@class Enumerable
@private
@deprecated Use native arrays and native array methods instead.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
interface Enumerable {}
const Enumerable = Mixin[INTERNAL_MIXIN_CREATE]();
const Enumerable = deprecatedMixin(Mixin[INTERNAL_MIXIN_CREATE](), () => {
deprecateUntil(
'The `Enumerable` mixin is deprecated. Use native arrays and native array methods instead.',
DEPRECATIONS.DEPRECATE_ENUMERABLE
);
});

export default Enumerable;
10 changes: 9 additions & 1 deletion packages/@ember/enumerable/mutable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Enumerable from '@ember/enumerable';
import Mixin from '@ember/object/mixin';
import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixin-create';
import { deprecatedMixin } from '@ember/-internals/utils/lib/deprecated-mixin';
import { deprecateUntil, DEPRECATIONS } from '@ember/-internals/deprecations';

/**
@module ember
Expand All @@ -15,9 +17,15 @@ import { INTERNAL_MIXIN_CREATE } from '@ember/-internals/utils/lib/internal-mixi
@namespace Ember
@uses Enumerable
@private
@deprecated Use native arrays and native array methods instead.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
interface MutableEnumerable extends Enumerable {}
const MutableEnumerable = Mixin[INTERNAL_MIXIN_CREATE](Enumerable);
const MutableEnumerable = deprecatedMixin(Mixin[INTERNAL_MIXIN_CREATE](Enumerable), () => {
deprecateUntil(
'The `MutableEnumerable` mixin is deprecated. Use native arrays and native array methods instead.',
DEPRECATIONS.DEPRECATE_ENUMERABLE
);
});

export default MutableEnumerable;
4 changes: 4 additions & 0 deletions packages/@ember/object/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { descriptorForProperty, isClassicDecorator } from '@ember/-internals/met
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 { deprecateAppliedMixins } from '@ember/-internals/utils/lib/deprecated-mixin';
import ActionHandler from '@ember/-internals/runtime/lib/mixins/action_handler';
import makeArray from '@ember/array/make';
import { assert } from '@ember/debug';
Expand Down Expand Up @@ -311,6 +312,7 @@ class CoreObject {
}

reopen(...args: Array<Mixin | Record<string, unknown>>): this {
deprecateAppliedMixins(args);
applyMixin(this, args);
return this;
}
Expand Down Expand Up @@ -713,6 +715,7 @@ class CoreObject {
...mixins: M
): Readonly<Statics> & EmberClassConstructor<Instance> & MergeArray<M>;
static extend(...mixins: any[]) {
deprecateAppliedMixins(mixins);
let Class = class extends this {};
reopen.apply(Class.PrototypeMixin, mixins);
return Class;
Expand Down Expand Up @@ -838,6 +841,7 @@ class CoreObject {
@public
*/
static reopen<C extends typeof CoreObject>(this: C, ...args: any[]): C {
deprecateAppliedMixins(args);
this.willReopen();
reopen.apply(this.PrototypeMixin, args);
return this;
Expand Down
7 changes: 6 additions & 1 deletion packages/@ember/object/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ 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 CoreObject {
static {
// `extend(Observable)` would fire the Observable deprecation for every app.
this.PrototypeMixin.reopen(Observable);
}

get _debugContainerKey() {
let factory = getFactoryFor(this);
return factory !== undefined && factory.fullName;
Expand Down
Loading
Loading