diff --git a/packages/@ember/-internals/environment/lib/env.ts b/packages/@ember/-internals/environment/lib/env.ts index 7e42a6f04ef..fab88cf6687 100644 --- a/packages/@ember/-internals/environment/lib/env.ts +++ b/packages/@ember/-internals/environment/lib/env.ts @@ -1,3 +1,4 @@ +import { FUNCTION_PROTOTYPE_EXTENSIONS } from '@ember/deprecated-features'; import global from './global'; /** @@ -155,12 +156,16 @@ export const ENV = { if (EXTEND_PROTOTYPES !== undefined) { if (typeof EXTEND_PROTOTYPES === 'object' && EXTEND_PROTOTYPES !== null) { ENV.EXTEND_PROTOTYPES.String = EXTEND_PROTOTYPES.String !== false; - ENV.EXTEND_PROTOTYPES.Function = EXTEND_PROTOTYPES.Function !== false; + if (FUNCTION_PROTOTYPE_EXTENSIONS) { + ENV.EXTEND_PROTOTYPES.Function = EXTEND_PROTOTYPES.Function !== false; + } ENV.EXTEND_PROTOTYPES.Array = EXTEND_PROTOTYPES.Array !== false; } else { let isEnabled = EXTEND_PROTOTYPES !== false; ENV.EXTEND_PROTOTYPES.String = isEnabled; - ENV.EXTEND_PROTOTYPES.Function = isEnabled; + if (FUNCTION_PROTOTYPE_EXTENSIONS) { + ENV.EXTEND_PROTOTYPES.Function = isEnabled; + } ENV.EXTEND_PROTOTYPES.Array = isEnabled; } } diff --git a/packages/@ember/-internals/metal/tests/observer_test.js b/packages/@ember/-internals/metal/tests/observer_test.js index ffa4ed59cf3..d84c06438c4 100644 --- a/packages/@ember/-internals/metal/tests/observer_test.js +++ b/packages/@ember/-internals/metal/tests/observer_test.js @@ -16,6 +16,7 @@ import { set, } from '..'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { FUNCTION_PROTOTYPE_EXTENSIONS } from '@ember/deprecated-features'; function K() {} @@ -103,18 +104,20 @@ moduleFor( assert.equal(observerCount, 10, 'should continue to fire indefinitely'); } - ['@test observer added declaratively via brace expansion should fire when property changes']( + ['@test observer added via Function.prototype extensions and brace expansion should fire when property changes']( assert ) { - if (ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS && ENV.EXTEND_PROTOTYPES.Function) { let obj = {}; let count = 0; - mixin(obj, { - observeFooAndBar: function() { - count++; - }.observes('{foo,bar}'), - }); + expectDeprecation(() => { + mixin(obj, { + observeFooAndBar: function() { + count++; + }.observes('{foo,bar}'), + }); + }, /Function prototype extensions have been deprecated, please migrate from function\(\){}.observes\('foo'\) to observer\('foo', function\(\) {}\)/); set(obj, 'foo', 'foo'); assert.equal(count, 1, 'observer specified via brace expansion invoked on property change'); @@ -129,10 +132,10 @@ moduleFor( } } - ['@test observer specified declaratively via brace expansion should fire when dependent property changes']( + ['@test observer specified via Function.prototype extensions via brace expansion should fire when dependent property changes']( assert ) { - if (ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS && ENV.EXTEND_PROTOTYPES.Function) { let obj = { baz: 'Initial' }; let count = 0; @@ -152,11 +155,13 @@ moduleFor( }) ); - mixin(obj, { - fooAndBarWatcher: function() { - count++; - }.observes('{foo,bar}'), - }); + expectDeprecation(() => { + mixin(obj, { + fooAndBarWatcher: function() { + count++; + }.observes('{foo,bar}'), + }); + }, /Function prototype extensions have been deprecated, please migrate from function\(\){}.observes\('foo'\) to observer\('foo', function\(\) {}\)/); get(obj, 'foo'); set(obj, 'baz', 'Baz'); diff --git a/packages/@ember/-internals/runtime/lib/ext/function.js b/packages/@ember/-internals/runtime/lib/ext/function.js index b3af2ee7649..e6ae6a11f7f 100644 --- a/packages/@ember/-internals/runtime/lib/ext/function.js +++ b/packages/@ember/-internals/runtime/lib/ext/function.js @@ -4,8 +4,10 @@ import { ENV } from '@ember/-internals/environment'; import { on, computed, observer } from '@ember/-internals/metal'; +import { deprecate } from '@ember/debug'; +import { FUNCTION_PROTOTYPE_EXTENSIONS } from '@ember/deprecated-features'; -if (ENV.EXTEND_PROTOTYPES.Function) { +if (FUNCTION_PROTOTYPE_EXTENSIONS && ENV.EXTEND_PROTOTYPES.Function) { Object.defineProperties(Function.prototype, { /** The `property` extension of Javascript's Function prototype is available @@ -77,6 +79,15 @@ if (ENV.EXTEND_PROTOTYPES.Function) { enumerable: false, writable: true, value: function() { + deprecate( + `Function prototype extensions have been deprecated, please migrate from function(){}.property('bar') to computed('bar', function() {}).`, + false, + { + id: 'function-prototype-extensions.property', + until: '4.0.0', + url: 'https://deprecations.emberjs.com/v3.x#toc_function-prototype-extensions-property', + } + ); return computed(...arguments, this); }, }, @@ -113,6 +124,16 @@ if (ENV.EXTEND_PROTOTYPES.Function) { enumerable: false, writable: true, value: function() { + deprecate( + `Function prototype extensions have been deprecated, please migrate from function(){}.observes('foo') to observer('foo', function() {}).`, + false, + { + id: 'function-prototype-extensions.observes', + until: '4.0.0', + url: 'https://deprecations.emberjs.com/v3.x#toc_function-prototype-extensions-observes', + } + ); + return observer(...arguments, this); }, }, @@ -147,6 +168,16 @@ if (ENV.EXTEND_PROTOTYPES.Function) { enumerable: false, writable: true, value: function() { + deprecate( + `Function prototype extensions have been deprecated, please migrate from function(){}.on('foo') to on('foo', function() {}).`, + false, + { + id: 'function-prototype-extensions.on', + until: '4.0.0', + url: 'https://deprecations.emberjs.com/v3.x#toc_function-prototype-extensions-on', + } + ); + return on(...arguments, this); }, }, diff --git a/packages/@ember/-internals/runtime/tests/ext/function_test.js b/packages/@ember/-internals/runtime/tests/ext/function_test.js index 68997ffa0ae..8ace67fe307 100644 --- a/packages/@ember/-internals/runtime/tests/ext/function_test.js +++ b/packages/@ember/-internals/runtime/tests/ext/function_test.js @@ -3,12 +3,13 @@ import { Mixin, mixin, get, set } from '@ember/-internals/metal'; import EmberObject from '../../lib/system/object'; import Evented from '../../lib/mixins/evented'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; +import { FUNCTION_PROTOTYPE_EXTENSIONS } from '@ember/deprecated-features'; moduleFor( 'Function.prototype.observes() helper', class extends AbstractTestCase { ['@test global observer helper takes multiple params'](assert) { - if (!ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS || !ENV.EXTEND_PROTOTYPES.Function) { assert.ok( 'undefined' === typeof Function.prototype.observes, 'Function.prototype helper disabled' @@ -16,13 +17,16 @@ moduleFor( return; } - let MyMixin = Mixin.create({ - count: 0, + let MyMixin; + expectDeprecation(() => { + MyMixin = Mixin.create({ + count: 0, - foo: function() { - set(this, 'count', get(this, 'count') + 1); - }.observes('bar', 'baz'), - }); + foo: function() { + set(this, 'count', get(this, 'count') + 1); + }.observes('bar', 'baz'), + }); + }, /Function prototype extensions have been deprecated, please migrate from function\(\){}.observes\('foo'\) to observer\('foo', function\(\) {}\)/); let obj = mixin({}, MyMixin); assert.equal(get(obj, 'count'), 0, 'should not invoke observer immediately'); @@ -38,7 +42,7 @@ moduleFor( 'Function.prototype.on() helper', class extends AbstractTestCase { ['@test sets up an event listener, and can trigger the function on multiple events'](assert) { - if (!ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS || !ENV.EXTEND_PROTOTYPES.Function) { assert.ok( 'undefined' === typeof Function.prototype.on, 'Function.prototype helper disabled' @@ -46,13 +50,16 @@ moduleFor( return; } - let MyMixin = Mixin.create({ - count: 0, + let MyMixin; + expectDeprecation(() => { + MyMixin = Mixin.create({ + count: 0, - foo: function() { - set(this, 'count', get(this, 'count') + 1); - }.on('bar', 'baz'), - }); + foo: function() { + set(this, 'count', get(this, 'count') + 1); + }.on('bar', 'baz'), + }); + }, /Function prototype extensions have been deprecated, please migrate from function\(\){}.on\('foo'\) to on\('foo', function\(\) {}\)/); let obj = mixin({}, Evented, MyMixin); assert.equal(get(obj, 'count'), 0, 'should not invoke listener immediately'); @@ -63,19 +70,22 @@ moduleFor( } ['@test can be chained with observes'](assert) { - if (!ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS || !ENV.EXTEND_PROTOTYPES.Function) { assert.ok('Function.prototype helper disabled'); return; } - let MyMixin = Mixin.create({ - count: 0, - bay: 'bay', - foo: function() { - set(this, 'count', get(this, 'count') + 1); - } - .observes('bay') - .on('bar'), + let MyMixin; + expectDeprecation(function() { + MyMixin = Mixin.create({ + count: 0, + bay: 'bay', + foo: function() { + set(this, 'count', get(this, 'count') + 1); + } + .observes('bay') + .on('bar'), + }); }); let obj = mixin({}, Evented, MyMixin); @@ -92,7 +102,7 @@ moduleFor( 'Function.prototype.property() helper', class extends AbstractTestCase { ['@test sets up a ComputedProperty'](assert) { - if (!ENV.EXTEND_PROTOTYPES.Function) { + if (!FUNCTION_PROTOTYPE_EXTENSIONS || !ENV.EXTEND_PROTOTYPES.Function) { assert.ok( 'undefined' === typeof Function.prototype.property, 'Function.prototype helper disabled' @@ -100,13 +110,16 @@ moduleFor( return; } - let MyClass = EmberObject.extend({ - firstName: null, - lastName: null, - fullName: function() { - return get(this, 'firstName') + ' ' + get(this, 'lastName'); - }.property('firstName', 'lastName'), - }); + let MyClass; + expectDeprecation(function() { + MyClass = EmberObject.extend({ + firstName: null, + lastName: null, + fullName: function() { + return get(this, 'firstName') + ' ' + get(this, 'lastName'); + }.property('firstName', 'lastName'), + }); + }, /Function prototype extensions have been deprecated, please migrate from function\(\){}.property\('bar'\) to computed\('bar', function\(\) {}\)/); let obj = MyClass.create({ firstName: 'Fred', lastName: 'Flinstone' }); assert.equal(get(obj, 'fullName'), 'Fred Flinstone', 'should return the computed value'); diff --git a/packages/@ember/deprecated-features/index.ts b/packages/@ember/deprecated-features/index.ts index b4acfa4fdc7..417ad0ebcfb 100644 --- a/packages/@ember/deprecated-features/index.ts +++ b/packages/@ember/deprecated-features/index.ts @@ -13,3 +13,4 @@ export const COMPONENT_MANAGER_STRING_LOOKUP = !!'3.8.0'; export const JQUERY_INTEGRATION = !!'3.9.0'; export const ALIAS_METHOD = !!'3.9.0'; export const APP_CTRL_ROUTER_PROPS = !!'3.10.0-beta.1'; +export const FUNCTION_PROTOTYPE_EXTENSIONS = !!'3.11.0-beta.1';