Skip to content

Commit

Permalink
Merge pull request #20730 from emberjs/kg-cleanup-extend-prototypes
Browse files Browse the repository at this point in the history
  • Loading branch information
kategengler committed Aug 22, 2024
2 parents 0377da6 + 7260196 commit 85a4f29
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 88 deletions.
8 changes: 0 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,6 @@ jobs:
- name: "Production build, with optional features"
BUILD: "production"
ENABLE_OPTIONAL_FEATURES: "true"
- name: "Extend prototypes"
EXTEND_PROTOTYPES: "true"
RAISE_ON_DEPRECATION: "false"
- name: "Extend prototypes, with optional features"
EXTEND_PROTOTYPES: "true"
ENABLE_OPTIONAL_FEATURES: "true"
RAISE_ON_DEPRECATION: "false"

steps:
- uses: actions/checkout@v4
Expand All @@ -115,7 +108,6 @@ jobs:
env:
ALL_DEPRECATIONS_ENABLED: ${{ matrix.ALL_DEPRECATIONS_ENABLED }}
OVERRIDE_DEPRECATION_VERSION: ${{ matrix.OVERRIDE_DEPRECATION_VERSION }}
EXTEND_PROTOTYPES: ${{ matrix.EXTEND_PROTOTYPES }}
ENABLE_OPTIONAL_FEATURES: ${{ matrix.ENABLE_OPTIONAL_FEATURES }}
RAISE_ON_DEPRECATION: ${{ matrix.RAISE_ON_DEPRECATION }}

Expand Down
4 changes: 0 additions & 4 deletions bin/run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ const variants = [
// hit its "until" version, the tests for it will behave correctly.
'OVERRIDE_DEPRECATION_VERSION',

// This enables the legacy Ember feature that causes Ember to extend built-in
// platform features like Array.
'EXTEND_PROTOTYPES',

// This enables all canary feature flags for unreleased feature within Ember
// itself.
'ENABLE_OPTIONAL_FEATURES',
Expand Down
3 changes: 0 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@
EmberENV.__test_hook_count__ += object;
});

// Handle extending prototypes
EmberENV['EXTEND_PROTOTYPES'] = !!QUnit.urlParams.EXTEND_PROTOTYPES;

// Handle testing feature flags
if (QUnit.urlParams.ENABLE_OPTIONAL_FEATURES) {
EmberENV.ENABLE_OPTIONAL_FEATURES = true;
Expand Down
16 changes: 7 additions & 9 deletions packages/@ember/-internals/environment/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,17 @@ export const ENV = {
native object prototypes, a few extra methods in order to provide a more
friendly API.
We generally recommend leaving this option set to true however, if you need
to turn it off, you can add the configuration property
`EXTEND_PROTOTYPES` to `EmberENV` and set it to `false`.
Note, when disabled (the default configuration for Ember Addons), you will
instead have to access all methods and functions from the Ember
namespace.
The behavior from setting this option to `true` was deprecated in Ember 5.10.
@property EXTEND_PROTOTYPES
@type Boolean
@default true
@for EmberENV
@public
@private
@deprecated in v5.10
*/
EXTEND_PROTOTYPES: {
Array: true,
Array: false,
},

/**
Expand Down Expand Up @@ -201,6 +196,9 @@ export const ENV = {
}
}

// TODO: Remove in Ember 6.5. This setting code for EXTEND_PROTOTYPES
// should stay for at least an LTS cycle so that users get the explicit
// deprecation exception when it breaks in >= 6.0.0.
let { EXTEND_PROTOTYPES } = EmberENV;
if (EXTEND_PROTOTYPES !== undefined) {
if (typeof EXTEND_PROTOTYPES === 'object' && EXTEND_PROTOTYPES !== null) {
Expand Down
54 changes: 16 additions & 38 deletions packages/@ember/array/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { assert } from '@ember/debug';
import Enumerable from '@ember/enumerable';
import MutableEnumerable from '@ember/enumerable/mutable';
import { compare, typeOf } from '@ember/utils';
import { ENV } from '@ember/-internals/environment';
import Observable from '@ember/object/observable';
import type { MethodNamesOf, MethodParams, MethodReturns } from '@ember/-internals/utility-types';
import type { ComputedPropertyCallback } from '@ember/-internals/metal';
Expand Down Expand Up @@ -1858,11 +1857,7 @@ const MutableArray = Mixin.create(EmberArray, MutableEnumerable, {

/**
Creates an `Ember.NativeArray` from an Array-like object.
Does not modify the original object's contents. `A()` is not needed if
`EmberENV.EXTEND_PROTOTYPES` is `true` (the default value). However,
it is recommended that you use `A()` when creating addons for
ember or when you can not guarantee that `EmberENV.EXTEND_PROTOTYPES`
will be `true`.
Does not modify the original object's contents.
Example
Expand Down Expand Up @@ -2061,10 +2056,7 @@ interface MutableArrayWithoutNative<T>

/**
The NativeArray mixin contains the properties needed to make the native
Array support MutableArray and all of its dependent APIs. Unless you
have `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Array` set to
false, this will be applied automatically. Otherwise you can apply the mixin
at anytime by calling `Ember.NativeArray.apply(Array.prototype)`.
Array support MutableArray and all of its dependent APIs.
@class Ember.NativeArray
@uses MutableArray
Expand Down Expand Up @@ -2101,34 +2093,20 @@ NativeArray = NativeArray.without(...ignore);

let A: <T>(arr?: Array<T>) => NativeArray<T>;

if (ENV.EXTEND_PROTOTYPES.Array) {
NativeArray.apply(Array.prototype, true);

A = function <T>(this: unknown, arr?: Array<T>) {
assert(
'You cannot create an Ember Array with `new A()`, please update to calling A as a function: `A()`',
!(this instanceof A)
);

// SAFTEY: Since we are extending prototypes all true native arrays are Ember NativeArrays
return (arr || []) as NativeArray<T>;
};
} else {
A = function <T>(this: unknown, arr?: Array<T>) {
assert(
'You cannot create an Ember Array with `new A()`, please update to calling A as a function: `A()`',
!(this instanceof A)
);

if (isEmberArray(arr)) {
// SAFETY: If it's a true native array and it is also an EmberArray then it should be an Ember NativeArray
return arr as unknown as NativeArray<T>;
} else {
// SAFETY: This will return an NativeArray but TS can't infer that.
return NativeArray.apply(arr ?? []) as NativeArray<T>;
}
};
}
A = function <T>(this: unknown, arr?: Array<T>) {
assert(
'You cannot create an Ember Array with `new A()`, please update to calling A as a function: `A()`',
!(this instanceof A)
);

if (isEmberArray(arr)) {
// SAFETY: If it's a true native array and it is also an EmberArray then it should be an Ember NativeArray
return arr as unknown as NativeArray<T>;
} else {
// SAFETY: This will return an NativeArray but TS can't infer that.
return NativeArray.apply(arr ?? []) as NativeArray<T>;
}
};

export { A, NativeArray, MutableArray };

Expand Down
24 changes: 12 additions & 12 deletions tests/node/app-boot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ QUnit.module('App Boot', function (hooks) {
QUnit.test('nested {{component}}', function (assert) {
this.template('index', '{{root-component}}');

this.template(
'components/root-component',
this.component(
'root-component',
{
location: 'World',
hasExistence: true,
},
"\
<h1>Hello {{#if this.hasExistence}}{{this.location}}{{/if}}</h1>\
<div>{{component 'foo-bar'}}</div>\
"
<h1>Hello {{#if this.hasExistence}}{{this.location}}{{/if}}</h1>\
<div>{{component 'foo-bar'}}</div>\
"
);

this.component('root-component', {
location: 'World',
hasExistence: true,
});

this.template(
'components/foo-bar',
this.component(
'foo-bar',
undefined,
'\
<p>The files are *inside* the computer?!</p>\
'
Expand Down
9 changes: 7 additions & 2 deletions tests/node/helpers/setup-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ module.exports = function (hooks) {

this.Ember = Ember;
this.compile = compile;
this.setComponentTemplate = Ember._setComponentTemplate;
this.templateOnlyComponent = Ember._templateOnlyComponent;

Ember.testing = true;

Expand Down Expand Up @@ -166,8 +168,11 @@ function registerTemplate(name, template) {
this.register('template:' + name, this.compile(template));
}

function registerComponent(name, componentProps) {
let component = this.Ember.Component.extend(componentProps);
function registerComponent(name, componentProps, templateContents) {
let component = this.setComponentTemplate(
this.compile(templateContents),
componentProps ? this.Ember.Component.extend(componentProps) : this.templateOnlyComponent()
);
this.register('component:' + name, component);
}

Expand Down
26 changes: 14 additions & 12 deletions tests/node/visit-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,24 @@ QUnit.module('Ember.Application - visit() Integration Tests', function (hooks) {
this.template('application', '<h1>Hello world</h1>\n{{outlet}}');
this.template('a', '<h2>Welcome to {{x-foo page="A"}}</h2>');
this.template('b', '<h2>{{x-foo page="B"}}</h2>');
this.template('components/x-foo', 'Page {{this.page}}');

let initCalled = false;
let didInsertElementCalled = false;

this.component('x-foo', {
tagName: 'span',
init: function () {
this._super();
initCalled = true;
},
didInsertElement: function () {
didInsertElementCalled = true;
this.component(
'x-foo',
{
tagName: 'span',
init: function () {
this._super();
initCalled = true;
},
didInsertElement: function () {
didInsertElementCalled = true;
},
},
});
'Page {{this.page}}'
);

let App = this.createApplication();

Expand Down Expand Up @@ -337,8 +340,7 @@ QUnit.module('Ember.Application - visit() Integration Tests', function (hooks) {

QUnit.test('FastBoot: tagless components can render', function (assert) {
this.template('application', "<div class='my-context'>{{my-component}}</div>");
this.component('my-component', { tagName: '' });
this.template('components/my-component', '<h1>hello world</h1>');
this.component('my-component', { tagName: '' }, '<h1>hello world</h1>');

let App = this.createApplication();

Expand Down

0 comments on commit 85a4f29

Please sign in to comment.