From a8721cae0b9b57afcf381a69b7ca8df20e810b1d Mon Sep 17 00:00:00 2001 From: hanli Date: Wed, 4 May 2022 00:32:01 -0700 Subject: [PATCH 01/22] [feat] Deprecate array prototype extensions --- .../deprecate-array-prototype-extensions.md | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 content/ember/v5/deprecate-array-prototype-extensions.md diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md new file mode 100644 index 00000000..a766ac87 --- /dev/null +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -0,0 +1,255 @@ +--- +id: deprecate-array-prototype-extensions +title: "Deprecate array prototype extensions" +until: '6.0.0' +since: '5.0.0' +--- + +Ember historically extended the prototypes of native Javascript arrays to implement `Ember.Enumerable`, `Ember.MutableEnumerable`, `Ember.MutableArray`, `Ember.Array`. As of v5, the usages of array prototype extensions are deprecated. + +For convenient functions like `filterBy`, `compact`, you can directly use native array method instead. + +For mutation functions (like `pushObject`, `replace`) or observable properties (`firstObject`, `lastObject`), in order to keep the reactive behavior, you should take following steps: +* 1. convert the array either to a new `@tracked` property, or use `TrackedArray` from `tracked-built-ins`; +* 2. convert the function to use array native methods; +* 3. fully test to make sure the reactivity is maintained. + +### Convenient Functions +Before: + +```js +const simpleArray = [1, 2, 3, undefined]; +const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; + +simpleArray.any(value => value === 1); // true +simpleArray.compact(); // [1, 2, 3] +complexArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }] +complexArray.findBy('isFruit'); // { food: 'apple', isFruit: true } +complexArray.getEach('food'); // ['apple', 'beans'] +complexArray.isAny('isFruit'); // true +complexArray.isEvery('isFruit'); // false +// To be added: +// invoke +// mapBy +// objectAt +// objectsAt +// reject +// rejectBy +// sortBy +// toArray +// uniq +// uniqBy +``` + +// setEach +// without + +After: + +```js +import { get } from '@ember/object'; +const simpleArray = [1, 2, 3, undefined]; +const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; + +simpleArray.some(value => value === 1); // true +simpleArray.filter(value => value !== null && value !== undefined);; // [1, 2, 3] +complexArray.filter(el => get(el, 'food') === 'beans'); // [{ food: 'beans', isFruit: false }] +complexArray.find(el => get(el, 'isFruit')); // { food: 'apple', isFruit: true } +complexArray.map(el => get(el, 'food')); // ['apple', 'beans'] +complexArray.any(el => el.isFruit); // true +complexArray.every(el => el.isFruit); // false +// To be added: +// invoke +// mapBy +// objectAt +// objectsAt +// reject +// rejectBy +// sortBy +// toArray +// uniq +// uniqBy +``` + +You may also instead rely on methods from another library like [lodash](https://lodash.com/). +Keep in mind that different libraries will behave in slightly different ways, so make sure any critical `Array` transformations are thoroughly tested. + +### Observable Properties +`firstObject`, `lastObject` are observable properties. Changing directly from `firstObject` to `at(0)` or `[0]` might cause issues that the properties are no longer reactive. + +If the `firstObject` and `lastObject` are used in a template, you can convert to use `get` helper. This is safe as `get` helper handles the reactivity: + +Before +```hbs + +``` + +After +```hbs + +``` + +You can also use fixer provided by [`ember-template-lint/no-array-prototype-extensions`](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-array-prototype-extensions.md). + +If the `firstObject` and `lastObject` are used in js files and you used them in an observable way, you will need to convert the array to `@tracked` or `TrackedArray`. + +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; + + // lastObject will change when abc.pushObject is executed + get lastObj() { + return abc.lastObject; + } + + @action + someAction(value) { + abc.pushObject(value); + } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(); + + get lastObj() { + return abc.at(-1); + } + + @action + someAction(value) { + abc.push(value); + } +} +``` + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = []; + + get lastObj() { + return abc.at(-1); + } + + @action + someAction(value) { + abc = [...abc, value]; + } +} +``` + +### Mutation methods +Mutation methods are observable-based, which means you should always convert the array to `@tracked` or `TrackedArray` in order to keep the reactivity. This includes following (a list from [`MutableArray` methods](https://api.emberjs.com/ember/4.3/classes/MutableArray)): + +* `without` +* `addObject` +* `addObjects` +* `clear` +* `insertAt` +* `popObject` +* `pushObject` +* `pushObjects` +* `removeAt` +* `removeObject` +* `removeObjects` +* `replace` +* `reverseObjects` +* `setObjects` +* `shiftObject` +* `unshiftObject` +* `unshiftObjects` + +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; + + @action + pushAction(value) { + abc.pushObject(value); + } + + @action + removeAction(value) { + abc.removeObject(value); + } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(); + + @action + pushAction(value) { + abc.push(value); + } + + @action + removeAction(value) { + let loc = abc.length || 0; + while (--loc >= 0) { + let curValue = abc.at(loc); + + if (curValue === value) { + abc.splice(loc, 1); + } + } + } +} +``` + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = []; + + @action + pushAction(value) { + abc = [...abc, value]; + } + + @action + removeAction(value) { + let loc = abc.length || 0; + while (--loc >= 0) { + let curValue = abc.at(loc); + + if (curValue === value) { + abc.splice(loc, 1); + } + } + abc = [...abc]; + } +} +``` + +It's always recommended to reference the existing implementation of the method you are trying to convert. This can make sure functionalities are kept as it was. Implementation details can be found in [`MutableArray`](https://api.emberjs.com/ember/release/classes/MutableArray), for example [`removeObject`](https://github.com/emberjs/ember.js/blob/v4.3.0/packages/%40ember/-internals/runtime/lib/mixins/array.js#L1619). + + + From fe11c6f18814aea03a8c3e435457347b153b4ac7 Mon Sep 17 00:00:00 2001 From: hanli Date: Wed, 4 May 2022 22:25:36 -0700 Subject: [PATCH 02/22] Add full convenient functions --- .../deprecate-array-prototype-extensions.md | 208 ++++++++++++++---- 1 file changed, 168 insertions(+), 40 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index a766ac87..8e8475b8 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -7,77 +7,204 @@ since: '5.0.0' Ember historically extended the prototypes of native Javascript arrays to implement `Ember.Enumerable`, `Ember.MutableEnumerable`, `Ember.MutableArray`, `Ember.Array`. As of v5, the usages of array prototype extensions are deprecated. -For convenient functions like `filterBy`, `compact`, you can directly use native array method instead. +For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. -For mutation functions (like `pushObject`, `replace`) or observable properties (`firstObject`, `lastObject`), in order to keep the reactive behavior, you should take following steps: +For mutation functions (like `pushObject`, `replace`) or observable properties (`firstObject`, `lastObject`), in order to keep the reactivity, you should take following steps: * 1. convert the array either to a new `@tracked` property, or use `TrackedArray` from `tracked-built-ins`; -* 2. convert the function to use array native methods; +* 2. use array native methods; * 3. fully test to make sure the reactivity is maintained. ### Convenient Functions +For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. This includes following (a list from [`EmberArray` methods](https://api.emberjs.com/ember/release/classes/EmberArray)): + +* `any` +* `compact` +* `filterBy` +* `findBy` +* `getEach` +* `invoke` +* `isAny` +* `isEvery` +* `mapBy` +* `objectAt` +* `objectsAt` +* `reject` +* `rejectBy` +* `sortBy` +* `toArray` +* `uniq` +* `uniqBy` + Before: ```js -const simpleArray = [1, 2, 3, undefined]; +const simpleArray = [1, 2, 3, undefined, 3]; const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; simpleArray.any(value => value === 1); // true -simpleArray.compact(); // [1, 2, 3] +simpleArray.compact(); // [1, 2, 3, 3] complexArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }] complexArray.findBy('isFruit'); // { food: 'apple', isFruit: true } complexArray.getEach('food'); // ['apple', 'beans'] complexArray.isAny('isFruit'); // true complexArray.isEvery('isFruit'); // false -// To be added: +complexArray.mapBy('food'); // ['apple', 'beans'] +simpleArray.objectAt(1) // 2 +simpleArray.objectsAt([1, 2]) // [2, 3] +complexArray.reject(el => el.isFruit) // [{ food: 'apple', isFruit: true }] +complexArray.rejectBy('isFruit') // [{ food: 'apple', isFruit: true }] +complexArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +simpleArray.uniq() // [1, 2, 3, undefined] +complexArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +complexArray.uniqBy('food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] + // invoke -// mapBy -// objectAt -// objectsAt -// reject -// rejectBy -// sortBy -// toArray -// uniq -// uniqBy -``` +class People { + name; + + constructor(name) { + this.name = name; + } + + greet(prefix = 'Hello') { + return `${prefix} ${this.name}`; + } +} -// setEach -// without +[new People('Tom'), new People('Joe')].invoke('greet', 'Hi'); // ['Hi Tom', 'Hi Joe'] +``` After: ```js import { get } from '@ember/object'; +import { sortBy, uniqBy } from 'lodash-es'; + const simpleArray = [1, 2, 3, undefined]; const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +// any simpleArray.some(value => value === 1); // true +// compact simpleArray.filter(value => value !== null && value !== undefined);; // [1, 2, 3] +// filterBy complexArray.filter(el => get(el, 'food') === 'beans'); // [{ food: 'beans', isFruit: false }] +// findBy complexArray.find(el => get(el, 'isFruit')); // { food: 'apple', isFruit: true } +// getEach complexArray.map(el => get(el, 'food')); // ['apple', 'beans'] +// isAny complexArray.any(el => el.isFruit); // true +// isEvery complexArray.every(el => el.isFruit); // false -// To be added: -// invoke // mapBy +complexArray.map(el => el.food); // ['apple', 'beans'] // objectAt +simpleArray[1] // 2 // objectsAt +[1, 3].map(index => simpleArray[index]); //[2, 3] // reject +complexArray.filter(el => !el.isFruit) // [{ food: 'apple', isFruit: true }] // rejectBy -// sortBy +complexArray.filter(el => !el.isFruit) // [{ food: 'apple', isFruit: true }] // toArray +[...complexArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // uniq +[...new Set(simpleArray)] // [1, 2, 3, undefined] + +// You may also instead rely on methods from another library like [lodash](https://lodash.com/). +// Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested. + +// sortBy +sortBy(complexArray, ['food', 'isFruit']); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // uniqBy +uniqBy(complexArray, 'food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] + +// invoke +class People { + name; + + constructor(name) { + this.name = name; + } + + greet(prefix = 'Hello') { + return `${prefix} ${this.name}`; + } +} + +[new People('Tom'), new People('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe'] ``` -You may also instead rely on methods from another library like [lodash](https://lodash.com/). -Keep in mind that different libraries will behave in slightly different ways, so make sure any critical `Array` transformations are thoroughly tested. +#### Some special cases +* `without` +before +```js +const simpleArray = ['a', 'b', 'c']; + +simpleArray.without('a'); // ['b', 'c'] +``` + +after +```js +const simpleArray = ['a', 'b', 'c']; + +simpleArray.filter(el => el !== 'a'); // ['b', 'c'] +``` + +Please make sure `without` reactivity is fully tested if needed. + +* `setEach` +`setEach` method internally implements `set` which responds to reactivity. You can either also use `set` or convert to `@tracked` properties. + +Before +```js +const items = [{ name: 'Joe' }, { name: 'Matt' }]; + +items.setEach('zipCode', '10011'); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }] +``` + +After +```js +// use `set` +import { set } from '@ember/object'; + +const items = [{ name: 'Joe' }, { name: 'Matt' }]; + +items.forEach(item => { + set(item, 'zipCode', '10011'); +}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }] +``` + +Or +```js +// use `@tracked` +import { tracked } from '@glimmer/tracking'; + +class Person { + name; + @tracked zipCode; + constructor({ name, zipCode }) { + this.name = name; + this.zipCode = zipCode; + } +} + +const items = new TrackedArray([ + new Person({ name: 'Joe' }), + new Person({ name: 'Matt' }), +]); + +items.forEach(item => { + item.zipCode = '10011'; +}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }] +``` ### Observable Properties `firstObject`, `lastObject` are observable properties. Changing directly from `firstObject` to `at(0)` or `[0]` might cause issues that the properties are no longer reactive. -If the `firstObject` and `lastObject` are used in a template, you can convert to use `get` helper. This is safe as `get` helper handles the reactivity: +#### Used in template +If the `firstObject` and `lastObject` are used in a template, you can convert to use `get` helper safely as `get` helper handles the reactivity already. Before ```hbs @@ -89,9 +216,10 @@ After ``` -You can also use fixer provided by [`ember-template-lint/no-array-prototype-extensions`](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-array-prototype-extensions.md). +You can also leverage fixers provided by [`ember-template-lint/no-array-prototype-extensions`](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-array-prototype-extensions.md). -If the `firstObject` and `lastObject` are used in js files and you used them in an observable way, you will need to convert the array to `@tracked` or `TrackedArray`. +#### Used in js +If the `firstObject` and `lastObject` are used in js files and you used them in an observable way, you will need to convert the accessors to `@tracked` array or `TrackedArray`. Before ```js @@ -99,7 +227,7 @@ import Component from '@glimmer/component'; export default class SampleComponent extends Component { abc = ['x', 'y', 'z', 'x']; - // lastObject will change when abc.pushObject is executed + // lastObj will change when `someAction` is executed get lastObj() { return abc.lastObject; } @@ -131,7 +259,7 @@ export default class SampleComponent extends Component { } } ``` - +Or ```js // @tracked import Component from '@glimmer/component'; @@ -153,7 +281,7 @@ export default class SampleComponent extends Component { ``` ### Mutation methods -Mutation methods are observable-based, which means you should always convert the array to `@tracked` or `TrackedArray` in order to keep the reactivity. This includes following (a list from [`MutableArray` methods](https://api.emberjs.com/ember/4.3/classes/MutableArray)): +Mutation methods are observable-based, which means you should always convert the accessors to `@tracked` or `TrackedArray` in order to maintain the reactivity. This includes following (a list from [`MutableArray` methods](https://api.emberjs.com/ember/4.3/classes/MutableArray)): * `without` * `addObject` @@ -181,12 +309,12 @@ export default class SampleComponent extends Component { @action pushAction(value) { - abc.pushObject(value); + this.abc.pushObject(value); } @action removeAction(value) { - abc.removeObject(value); + this.abc.removeObject(value); } } ``` @@ -203,17 +331,17 @@ export default class SampleComponent extends Component { @action pushAction(value) { - abc.push(value); + this.abc.push(value); } @action removeAction(value) { - let loc = abc.length || 0; + let loc = this.abc.length || 0; while (--loc >= 0) { - let curValue = abc.at(loc); + let curValue = this.abc.at(loc); if (curValue === value) { - abc.splice(loc, 1); + this.abc.splice(loc, 1); } } } @@ -231,20 +359,20 @@ export default class SampleComponent extends Component { @action pushAction(value) { - abc = [...abc, value]; + this.abc = [...this.abc, value]; } @action removeAction(value) { - let loc = abc.length || 0; + let loc = this.abc.length || 0; while (--loc >= 0) { - let curValue = abc.at(loc); + let curValue = this.abc.at(loc); if (curValue === value) { - abc.splice(loc, 1); + this.abc.splice(loc, 1); } } - abc = [...abc]; + this.abc = [...this.abc]; } } ``` From 83ab592fd60c56a76915abbf0d206185c4575d8d Mon Sep 17 00:00:00 2001 From: hanli Date: Wed, 4 May 2022 22:33:47 -0700 Subject: [PATCH 03/22] Rephrase and change formatting --- .../deprecate-array-prototype-extensions.md | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 8e8475b8..4ebbd676 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -10,9 +10,9 @@ Ember historically extended the prototypes of native Javascript arrays to implem For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. For mutation functions (like `pushObject`, `replace`) or observable properties (`firstObject`, `lastObject`), in order to keep the reactivity, you should take following steps: -* 1. convert the array either to a new `@tracked` property, or use `TrackedArray` from `tracked-built-ins`; -* 2. use array native methods; -* 3. fully test to make sure the reactivity is maintained. +1. convert the array either to a new `@tracked` property, or use `TrackedArray` from `tracked-built-ins`; +2. use array native methods; +3. fully test to make sure the reactivity is maintained. ### Convenient Functions For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. This includes following (a list from [`EmberArray` methods](https://api.emberjs.com/ember/release/classes/EmberArray)): @@ -137,24 +137,24 @@ class People { ``` #### Some special cases -* `without` -before +##### `without` +Before ```js const simpleArray = ['a', 'b', 'c']; simpleArray.without('a'); // ['b', 'c'] ``` -after +After ```js const simpleArray = ['a', 'b', 'c']; simpleArray.filter(el => el !== 'a'); // ['b', 'c'] ``` -Please make sure `without` reactivity is fully tested if needed. +Please make sure `without` reactivity is fully tested. -* `setEach` +##### `setEach` `setEach` method internally implements `set` which responds to reactivity. You can either also use `set` or convert to `@tracked` properties. Before @@ -176,7 +176,7 @@ items.forEach(item => { }); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }] ``` -Or +or ```js // use `@tracked` import { tracked } from '@glimmer/tracking'; @@ -229,12 +229,12 @@ export default class SampleComponent extends Component { // lastObj will change when `someAction` is executed get lastObj() { - return abc.lastObject; + return this.abc.lastObject; } @action someAction(value) { - abc.pushObject(value); + this.abc.pushObject(value); } } ``` @@ -250,16 +250,17 @@ export default class SampleComponent extends Component { abc = new TrackedArray(); get lastObj() { - return abc.at(-1); + return this.abc.at(-1); } @action someAction(value) { - abc.push(value); + this.abc.push(value); } } ``` -Or +or + ```js // @tracked import Component from '@glimmer/component'; @@ -270,12 +271,12 @@ export default class SampleComponent extends Component { @tracked abc = []; get lastObj() { - return abc.at(-1); + return this.abc.at(-1); } @action someAction(value) { - abc = [...abc, value]; + this.abc = [...this.abc, value]; } } ``` @@ -347,6 +348,7 @@ export default class SampleComponent extends Component { } } ``` +or ```js // @tracked From 5d654f50f4cdebc8f264a311e69fe57945c8e7df Mon Sep 17 00:00:00 2001 From: hanli Date: Wed, 4 May 2022 22:36:02 -0700 Subject: [PATCH 04/22] Use person instead --- content/ember/v5/deprecate-array-prototype-extensions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 4ebbd676..1389ca47 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -59,7 +59,7 @@ complexArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { complexArray.uniqBy('food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // invoke -class People { +class Person { name; constructor(name) { @@ -71,7 +71,7 @@ class People { } } -[new People('Tom'), new People('Joe')].invoke('greet', 'Hi'); // ['Hi Tom', 'Hi Joe'] +[new Person('Tom'), new Person('Joe')].invoke('greet', 'Hi'); // ['Hi Tom', 'Hi Joe'] ``` After: @@ -121,7 +121,7 @@ sortBy(complexArray, ['food', 'isFruit']); // [{ food: 'apple', isFruit: true }, uniqBy(complexArray, 'food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // invoke -class People { +class Person { name; constructor(name) { @@ -133,7 +133,7 @@ class People { } } -[new People('Tom'), new People('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe'] +[new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe'] ``` #### Some special cases From 86b06e2f5471b1a12179cde0ce497ceff2514e56 Mon Sep 17 00:00:00 2001 From: hanli Date: Fri, 17 Jun 2022 10:47:26 -0700 Subject: [PATCH 05/22] Add all other method for mutableArray --- .../deprecate-array-prototype-extensions.md | 263 +++++++++++++++++- 1 file changed, 250 insertions(+), 13 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 1389ca47..24740df9 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -49,12 +49,12 @@ complexArray.getEach('food'); // ['apple', 'beans'] complexArray.isAny('isFruit'); // true complexArray.isEvery('isFruit'); // false complexArray.mapBy('food'); // ['apple', 'beans'] -simpleArray.objectAt(1) // 2 -simpleArray.objectsAt([1, 2]) // [2, 3] -complexArray.reject(el => el.isFruit) // [{ food: 'apple', isFruit: true }] -complexArray.rejectBy('isFruit') // [{ food: 'apple', isFruit: true }] +simpleArray.objectAt(1); // 2 +simpleArray.objectsAt([1, 2]); // [2, 3] +complexArray.reject(el => el.isFruit); // [{ food: 'apple', isFruit: true }] +complexArray.rejectBy('isFruit'); // [{ food: 'apple', isFruit: true }] complexArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -simpleArray.uniq() // [1, 2, 3, undefined] +simpleArray.uniq(); // [1, 2, 3, undefined] complexArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] complexArray.uniqBy('food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] @@ -86,7 +86,7 @@ const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit // any simpleArray.some(value => value === 1); // true // compact -simpleArray.filter(value => value !== null && value !== undefined);; // [1, 2, 3] +simpleArray.filter(value => value !== null && value !== undefined); // [1, 2, 3] // filterBy complexArray.filter(el => get(el, 'food') === 'beans'); // [{ food: 'beans', isFruit: false }] // findBy @@ -104,21 +104,37 @@ simpleArray[1] // 2 // objectsAt [1, 3].map(index => simpleArray[index]); //[2, 3] // reject -complexArray.filter(el => !el.isFruit) // [{ food: 'apple', isFruit: true }] +complexArray.filter(el => !el.isFruit); // [{ food: 'apple', isFruit: true }] // rejectBy -complexArray.filter(el => !el.isFruit) // [{ food: 'apple', isFruit: true }] +complexArray.filter(el => !el.isFruit); // [{ food: 'apple', isFruit: true }] // toArray [...complexArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // uniq [...new Set(simpleArray)] // [1, 2, 3, undefined] +// uniqBy +complexArray.reduce( + (unique, item) => { + if (!unique.find(i => item[prop] === i[prop])) { + unique.push(item); + } + return unique; + }, + [] +); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +// sortBy +[...complexArray].sort((a, b) => { + return a.food?.localCompare(b.food) + ? a.food?.localCompare(b.food) + : a.isFruit - b.isFruit; +}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // You may also instead rely on methods from another library like [lodash](https://lodash.com/). // Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested. -// sortBy -sortBy(complexArray, ['food', 'isFruit']); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // uniqBy uniqBy(complexArray, 'food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +// sortBy +sortBy(complexArray, ['food', 'isFruit']); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] // invoke class Person { @@ -284,7 +300,6 @@ export default class SampleComponent extends Component { ### Mutation methods Mutation methods are observable-based, which means you should always convert the accessors to `@tracked` or `TrackedArray` in order to maintain the reactivity. This includes following (a list from [`MutableArray` methods](https://api.emberjs.com/ember/4.3/classes/MutableArray)): -* `without` * `addObject` * `addObjects` * `clear` @@ -308,15 +323,85 @@ import Component from '@glimmer/component'; export default class SampleComponent extends Component { abc = ['x', 'y', 'z', 'x']; + @action + addObject(value) { + this.abc.addObject(value); + } + + @action + addObjects(values) { + this.abc.addObjects(values); + } + + @action + clear() { + this.abc.clear(); + } + + @action + insertAt(idx, value) { + this.abc.insertAt(idx, value); + } + + @action + popObject() { + this.abc.popObject(); + } + @action pushAction(value) { this.abc.pushObject(value); } + @action + pushObjects(values) { + this.abc.pushObjects(values); + } + + @action + removeAt(start, len) { + this.abc.removeAt(start, len); + } + @action removeAction(value) { this.abc.removeObject(value); } + + @action + removeObjects(values) { + this.abc.removeObjects(values); + } + + @action + replace(idx, len, values) { + this.abc.replace(idx, len, values); + } + + @action + reverseObjects() { + this.abc.reverseObjects(); + } + + @action + setObjects(values) { + this.abc.setObjects(values); + } + + @action + shiftObject() { + this.abc.shiftObject(); + } + + @action + unshiftObject(obj) { + this.abc.unshiftObject(obj); + } + + @action + unshiftObjects(objs) { + this.abc.unshiftObjects(objs); + } } ``` @@ -330,13 +415,50 @@ import { TrackedArray } from 'tracked-built-ins'; export default class SampleComponent extends Component { abc = new TrackedArray(); + @action + addObject(value) { + if (!this.abc.includes(value)) { + this.abc.push(value); + } + } + + @action + addObjects(values) { + values.forEach(v => this.addObject(v)) + } + + @action + clear() { + this.abc.splice(0, this.abc.length); + } + + @action + insertAt(idx, value) { + this.abc.splice(idx, 0, value); + } + + @action + popObject() { + this.abc.pop(); + } + @action pushAction(value) { this.abc.push(value); } @action - removeAction(value) { + pushObjects(values) { + this.abc.splice(this.abc.length, 0, ...values); + } + + @action + removeAt(start, len) { + this.abc.splice(start, len); + } + + @action + removeObject(value) { let loc = this.abc.length || 0; while (--loc >= 0) { let curValue = this.abc.at(loc); @@ -346,6 +468,43 @@ export default class SampleComponent extends Component { } } } + + @action + removeObjects(values) { + values.forEach(v => { + this.removeObject(v); + }) + } + + @action + replace(idx, len, values) { + this.abc.splice(idx, len, ...values); + } + + @action + reverseObjects() { + this.abc.reverse(); + } + + @action + setObjects(values) { + this.abc.splice(0, this.abc.length, ...values); + } + + @action + shiftObject() { + this.abc.shift(); + } + + @action + unshiftObject(obj) { + this.abc.unshift(obj); + } + + @action + unshiftObjects(objs) { + this.abc.unshift(...objs); + } } ``` or @@ -359,13 +518,52 @@ import { tracked } from '@glimmer/tracking'; export default class SampleComponent extends Component { @tracked abc = []; + @action + addObject(value) { + if (!this.abc.includes(value)) { + [...this.abc, value]; + } + } + + @action + addObjects(values) { + values.forEach(v => this.addObject(v)) + } + + @action + clear() { + this.abc = []; + } + + @action + insertAt(idx, value) { + this.abc = [...this.abc.slice(0, idx), value, this.abc.slice(this.abc.length - idx)] + } + + @action + popObject() { + this.abc.pop(); + this.abc = [...this.abc]; + } + @action pushAction(value) { this.abc = [...this.abc, value]; } @action - removeAction(value) { + pushObjects(values) { + this.abc = [...this.abc, ...values]; + } + + @action + removeAt(start, len) { + this.abc.splice(start, len); + this.abc = [...this.abc]; + } + + @action + removeObject(value) { let loc = this.abc.length || 0; while (--loc >= 0) { let curValue = this.abc.at(loc); @@ -376,6 +574,45 @@ export default class SampleComponent extends Component { } this.abc = [...this.abc]; } + + @action + removeObjects(values) { + values.forEach(v => { + this.removeObject(v); + }) + this.abc = [...this.abc]; + } + + @action + replace(idx, len, values) { + this.abc.splice(idx, len, ...values); + this.abc = [...this.abc]; + } + + @action + reverseObjects() { + this.abc = [...this.abc.reverse()]; + } + + @action + setObjects(values) { + this.abc = [...values]; + } + + @action + shiftObject() { + this.abc = [...this.abc.shift()]; + } + + @action + unshiftObject(obj) { + this.abc = [...this.abc.unshift(obj)]; + } + + @action + unshiftObjects(objs) { + this.abc = [...this.abc.unshift(...objs)]; + } } ``` From e0f5c204fcd948f7e57340df3099ddd4534940ef Mon Sep 17 00:00:00 2001 From: hanli Date: Mon, 22 Aug 2022 22:12:50 -0700 Subject: [PATCH 06/22] Separate examples per metho --- .../deprecate-array-prototype-extensions.md | 336 ++++++++++++------ 1 file changed, 231 insertions(+), 105 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 24740df9..0aa12c6a 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -17,48 +17,72 @@ For mutation functions (like `pushObject`, `replace`) or observable properties ( ### Convenient Functions For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. This includes following (a list from [`EmberArray` methods](https://api.emberjs.com/ember/release/classes/EmberArray)): -* `any` -* `compact` -* `filterBy` -* `findBy` -* `getEach` -* `invoke` -* `isAny` -* `isEvery` -* `mapBy` -* `objectAt` -* `objectsAt` -* `reject` -* `rejectBy` -* `sortBy` -* `toArray` -* `uniq` -* `uniqBy` +##### `any` Before: +```js +someArray.any(callbackFn); +``` + +After: +```js +someArray.some(callbackFn); +``` +##### `compact` +Before: +```js +someArray.compact(); +``` + +After: +```js +someArray.filter(val => val !=== undefined && val !== null); +``` +##### `filterBy` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.filter(el => el.food === 'beans'); // [{ food: 'beans', isFruit: false }] +``` +##### `findBy` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.findBy('isFruit'); // { food: 'apple', isFruit: true } +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.find(el => e.isFruit); // { food: 'apple', isFruit: true } +``` +##### `getEach` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.getEach('food'); // ['apple', 'beans'] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.map(el => el.food); // ['apple', 'beans'] +``` + +##### `invoke` + +Before: ```js -const simpleArray = [1, 2, 3, undefined, 3]; -const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; - -simpleArray.any(value => value === 1); // true -simpleArray.compact(); // [1, 2, 3, 3] -complexArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }] -complexArray.findBy('isFruit'); // { food: 'apple', isFruit: true } -complexArray.getEach('food'); // ['apple', 'beans'] -complexArray.isAny('isFruit'); // true -complexArray.isEvery('isFruit'); // false -complexArray.mapBy('food'); // ['apple', 'beans'] -simpleArray.objectAt(1); // 2 -simpleArray.objectsAt([1, 2]); // [2, 3] -complexArray.reject(el => el.isFruit); // [{ food: 'apple', isFruit: true }] -complexArray.rejectBy('isFruit'); // [{ food: 'apple', isFruit: true }] -complexArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -simpleArray.uniq(); // [1, 2, 3, undefined] -complexArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -complexArray.uniqBy('food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] - -// invoke class Person { name; @@ -75,68 +99,7 @@ class Person { ``` After: - ```js -import { get } from '@ember/object'; -import { sortBy, uniqBy } from 'lodash-es'; - -const simpleArray = [1, 2, 3, undefined]; -const complexArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; - -// any -simpleArray.some(value => value === 1); // true -// compact -simpleArray.filter(value => value !== null && value !== undefined); // [1, 2, 3] -// filterBy -complexArray.filter(el => get(el, 'food') === 'beans'); // [{ food: 'beans', isFruit: false }] -// findBy -complexArray.find(el => get(el, 'isFruit')); // { food: 'apple', isFruit: true } -// getEach -complexArray.map(el => get(el, 'food')); // ['apple', 'beans'] -// isAny -complexArray.any(el => el.isFruit); // true -// isEvery -complexArray.every(el => el.isFruit); // false -// mapBy -complexArray.map(el => el.food); // ['apple', 'beans'] -// objectAt -simpleArray[1] // 2 -// objectsAt -[1, 3].map(index => simpleArray[index]); //[2, 3] -// reject -complexArray.filter(el => !el.isFruit); // [{ food: 'apple', isFruit: true }] -// rejectBy -complexArray.filter(el => !el.isFruit); // [{ food: 'apple', isFruit: true }] -// toArray -[...complexArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -// uniq -[...new Set(simpleArray)] // [1, 2, 3, undefined] -// uniqBy -complexArray.reduce( - (unique, item) => { - if (!unique.find(i => item[prop] === i[prop])) { - unique.push(item); - } - return unique; - }, - [] -); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -// sortBy -[...complexArray].sort((a, b) => { - return a.food?.localCompare(b.food) - ? a.food?.localCompare(b.food) - : a.isFruit - b.isFruit; -}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] - -// You may also instead rely on methods from another library like [lodash](https://lodash.com/). -// Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested. - -// uniqBy -uniqBy(complexArray, 'food'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] -// sortBy -sortBy(complexArray, ['food', 'isFruit']); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] - -// invoke class Person { name; @@ -151,21 +114,184 @@ class Person { [new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe'] ``` +##### `isAny` + +Before +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.isAny('isFruit'); // true +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.any(el => el.isFruit); // true +``` + +##### `isEvery` +Before: + +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.isEvery('isFruit'); // false +``` + +After: + +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.every(el => el.isFruit); // false +``` +##### `mapBy` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.mapBy('food'); // ['apple', 'beans'] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.map(el => el.food); // ['apple', 'beans'] +``` + +##### `objectAt` + +Before +```js +const someArray = [1, 2, 3, undefined]; +someArray.objectAt(1); // 2 +``` + +After: +```js +const someArray = [1, 2, 3, undefined]; +someArray[1] // 2 +``` + +##### `objectsAt` + +Before: +```js +const someArray = [1, 2, 3, undefined]; +someArray.objectsAt([1, 2]); // [2, 3] +``` + +After: +```js +const someArray = [1, 2, 3, undefined]; +[1, 2].map(index => someArray[index]); //[2, 3] +``` + +##### `reject` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.reject(el => el.isFruit); // [{ food: 'beans', isFruit: false }] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }] +``` +##### `rejectBy` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.rejectBy('isFruit'); // [{ food: 'beans', isFruit: false }] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }] +``` +##### `sortBy` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +[...someArray].sort((a, b) => { + return a.food?.localCompare(b.food) + ? a.food?.localCompare(b.food) + : a.isFruit - b.isFruit; +}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +``` +##### `toArray` + +Before: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +someArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +``` + +After: +```js +const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; +[...someArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] +``` + +##### `uniq` + +Before: +```js +const someArray = [1, 2, 3, undefined, 3]; +someArray.uniq(); // [1, 2, 3, undefined] +``` + +After: +```js +const someArray = [1, 2, 3, undefined, 3]; +[...new Set(someArray)] // [1, 2, 3, undefined] +``` + +##### `uniqBy` + +Before: +```js +const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }]; +someArray.uniqBy('food'); // [{ food: 'apple' }, { food: 'beans' }] +``` + +After: +```js +const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }]; +someArray.reduce( + (unique, item) => { + if (!unique.find(i => item.food === i.food)) { + unique.push(item); + } + return unique; + }, + [] +); // [{ food: 'apple' }, { food: 'beans' }] +``` + +You may also instead rely on methods from another library like [lodash](https://lodash.com/). +Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested. #### Some special cases ##### `without` Before ```js -const simpleArray = ['a', 'b', 'c']; - -simpleArray.without('a'); // ['b', 'c'] +const someArray = ['a', 'b', 'c']; +someArray.without('a'); // ['b', 'c'] ``` After ```js -const simpleArray = ['a', 'b', 'c']; - -simpleArray.filter(el => el !== 'a'); // ['b', 'c'] +const someArray = ['a', 'b', 'c']; +someArray.filter(el => el !== 'a'); // ['b', 'c'] ``` Please make sure `without` reactivity is fully tested. From 9a0da278038d586aa2845749322459a76c15ec5a Mon Sep 17 00:00:00 2001 From: hanli Date: Wed, 31 Aug 2022 22:40:21 -0700 Subject: [PATCH 07/22] Separate before/after per method for mutation methods --- .../deprecate-array-prototype-extensions.md | 883 ++++++++++++++---- 1 file changed, 700 insertions(+), 183 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 0aa12c6a..c686941d 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -14,10 +14,10 @@ For mutation functions (like `pushObject`, `replace`) or observable properties ( 2. use array native methods; 3. fully test to make sure the reactivity is maintained. -### Convenient Functions +## Convenient Functions For convenient functions like `filterBy`, `compact`, you can directly convert to use native array methods. This includes following (a list from [`EmberArray` methods](https://api.emberjs.com/ember/release/classes/EmberArray)): -##### `any` +#### `any` Before: ```js @@ -28,7 +28,7 @@ After: ```js someArray.some(callbackFn); ``` -##### `compact` +#### `compact` Before: ```js @@ -39,7 +39,7 @@ After: ```js someArray.filter(val => val !=== undefined && val !== null); ``` -##### `filterBy` +#### `filterBy` Before: ```js @@ -52,7 +52,7 @@ After: const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; someArray.filter(el => el.food === 'beans'); // [{ food: 'beans', isFruit: false }] ``` -##### `findBy` +#### `findBy` Before: ```js @@ -65,7 +65,7 @@ After: const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; someArray.find(el => e.isFruit); // { food: 'apple', isFruit: true } ``` -##### `getEach` +#### `getEach` Before: ```js @@ -79,7 +79,7 @@ const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: f someArray.map(el => el.food); // ['apple', 'beans'] ``` -##### `invoke` +#### `invoke` Before: ```js @@ -114,7 +114,7 @@ class Person { [new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe'] ``` -##### `isAny` +#### `isAny` Before ```js @@ -128,7 +128,7 @@ const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: f someArray.any(el => el.isFruit); // true ``` -##### `isEvery` +#### `isEvery` Before: ```js @@ -142,7 +142,7 @@ After: const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; someArray.every(el => el.isFruit); // false ``` -##### `mapBy` +#### `mapBy` Before: ```js @@ -156,7 +156,7 @@ const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: f someArray.map(el => el.food); // ['apple', 'beans'] ``` -##### `objectAt` +#### `objectAt` Before ```js @@ -170,7 +170,7 @@ const someArray = [1, 2, 3, undefined]; someArray[1] // 2 ``` -##### `objectsAt` +#### `objectsAt` Before: ```js @@ -184,7 +184,7 @@ const someArray = [1, 2, 3, undefined]; [1, 2].map(index => someArray[index]); //[2, 3] ``` -##### `reject` +#### `reject` Before: ```js @@ -197,7 +197,7 @@ After: const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }] ``` -##### `rejectBy` +#### `rejectBy` Before: ```js @@ -210,7 +210,7 @@ After: const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }] ``` -##### `sortBy` +#### `sortBy` Before: ```js @@ -227,7 +227,7 @@ const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: f : a.isFruit - b.isFruit; }); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] ``` -##### `toArray` +#### `toArray` Before: ```js @@ -241,7 +241,7 @@ const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: f [...someArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] ``` -##### `uniq` +#### `uniq` Before: ```js @@ -255,7 +255,7 @@ const someArray = [1, 2, 3, undefined, 3]; [...new Set(someArray)] // [1, 2, 3, undefined] ``` -##### `uniqBy` +#### `uniqBy` Before: ```js @@ -280,8 +280,8 @@ someArray.reduce( You may also instead rely on methods from another library like [lodash](https://lodash.com/). Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested. -#### Some special cases -##### `without` +### Some special cases +#### `without` Before ```js const someArray = ['a', 'b', 'c']; @@ -296,7 +296,7 @@ someArray.filter(el => el !== 'a'); // ['b', 'c'] Please make sure `without` reactivity is fully tested. -##### `setEach` +#### `setEach` `setEach` method internally implements `set` which responds to reactivity. You can either also use `set` or convert to `@tracked` properties. Before @@ -342,10 +342,10 @@ items.forEach(item => { }); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }] ``` -### Observable Properties +## Observable Properties `firstObject`, `lastObject` are observable properties. Changing directly from `firstObject` to `at(0)` or `[0]` might cause issues that the properties are no longer reactive. -#### Used in template +### Used in template If the `firstObject` and `lastObject` are used in a template, you can convert to use `get` helper safely as `get` helper handles the reactivity already. Before @@ -360,7 +360,7 @@ After You can also leverage fixers provided by [`ember-template-lint/no-array-prototype-extensions`](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-array-prototype-extensions.md). -#### Used in js +### Used in js If the `firstObject` and `lastObject` are used in js files and you used them in an observable way, you will need to convert the accessors to `@tracked` array or `TrackedArray`. Before @@ -423,25 +423,10 @@ export default class SampleComponent extends Component { } ``` -### Mutation methods +## Mutation methods Mutation methods are observable-based, which means you should always convert the accessors to `@tracked` or `TrackedArray` in order to maintain the reactivity. This includes following (a list from [`MutableArray` methods](https://api.emberjs.com/ember/4.3/classes/MutableArray)): -* `addObject` -* `addObjects` -* `clear` -* `insertAt` -* `popObject` -* `pushObject` -* `pushObjects` -* `removeAt` -* `removeObject` -* `removeObjects` -* `replace` -* `reverseObjects` -* `setObjects` -* `shiftObject` -* `unshiftObject` -* `unshiftObjects` +#### `addObject` Before ```js @@ -453,80 +438,594 @@ export default class SampleComponent extends Component { addObject(value) { this.abc.addObject(value); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + addObject(value) { + if (!this.abc.includes(value)) { + this.abc.push(value); + } + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + addObject(value) { + if (!this.abc.includes(value)) { + this.abc = [...this.abc, value]; + } + } +} +``` +#### `addObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; + + @action + addObjects(value) { + this.abc.addObjects(value); + } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + _addObject(value) { + if (!this.abc.includes(value)) { + this.abc.push(value); + } + } + + @action + addObjects(values) { + values.forEach(v => this._addObject(v)) + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + _addObject(value) { + if (!this.abc.includes(value)) { + this.abc = [...this.abc, value]; + } + } @action addObjects(values) { - this.abc.addObjects(values); + values.forEach(v => this._addObject(v)) } +} +``` + +#### `clear` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - clear() { + clear(value) { this.abc.clear(); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + clear(value) { + this.abc.splice(0, this.abc.length); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + clear() { + this.abc = []; + } +} +``` + +#### `insertAt` + +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action insertAt(idx, value) { this.abc.insertAt(idx, value); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + insertAt(idx, value) { + this.abc.splice(idx, 0, value); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + insertAt(idx, value) { + this.abc = [...this.abc.slice(0, idx), value, this.abc.slice(this.abc.length - idx)] + } +} +``` + +#### `popObject` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action popObject() { this.abc.popObject(); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + popObject() { + this.abc.pop(); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + popObject() { + this.abc.pop(); + this.abc = [...this.abc]; + } +} +``` + +#### `pushObject` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - pushAction(value) { + pushObject(value) { this.abc.pushObject(value); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + pushObject(value) { + this.abc.push(value); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + pushObject(value) { + this.abc = [...this.abc, value]; + } +} +``` + +#### `pushObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action pushObjects(values) { this.abc.pushObjects(values); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + pushObjects(values) { + this.abc.splice(this.abc.length, 0, ...values); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + pushObjects(values) { + this.abc = [...this.abc, ...values]; + } +} +``` + +#### `removeAt` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action removeAt(start, len) { this.abc.removeAt(start, len); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + removeAt(start, len) { + this.abc.splice(start, len); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + removeAt(start, len) { + this.abc.splice(start, len); + this.abc = [...this.abc]; + } +} +``` + +#### `removeObject` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - removeAction(value) { + removeObject(value) { this.abc.removeObject(value); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + @action + removeObject(value) { + let loc = this.abc.length || 0; + while (--loc >= 0) { + let curValue = this.abc.at(loc); + + if (curValue === value) { + this.abc.splice(loc, 1); + } + } + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + @action + removeObject(value) { + let loc = this.abc.length || 0; + while (--loc >= 0) { + let curValue = this.abc.at(loc); + + if (curValue === value) { + this.abc.splice(loc, 1); + } + } + this.abc = [...this.abc]; + } +} +``` + +#### `removeObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action removeObjects(values) { this.abc.removeObjects(values); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); + + _removeObject(value) { + let loc = this.abc.length || 0; + while (--loc >= 0) { + let curValue = this.abc.at(loc); + + if (curValue === value) { + this.abc.splice(loc, 1); + } + } + } @action - replace(idx, len, values) { - this.abc.replace(idx, len, values); + removeObjects(values) { + values.forEach(v => { + this._removeObject(v); + }); + } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; + + _removeObject(value) { + let loc = this.abc.length || 0; + while (--loc >= 0) { + let curValue = this.abc.at(loc); + + if (curValue === value) { + this.abc.splice(loc, 1); + } + } + this.abc = [...this.abc]; } @action - reverseObjects() { - this.abc.reverseObjects(); + removeObjects(values) { + values.forEach(v => { + this._removeObject(v); + }) } +} +``` + +#### `replace` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - setObjects(values) { - this.abc.setObjects(values); + replace(idx, len, values) { + this.abc.replace(idx, len, values); } +} +``` + +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - shiftObject() { - this.abc.shiftObject(); + replace(idx, len, values) { + this.abc.splice(idx, len, ...values); } +} +``` +or + +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; @action - unshiftObject(obj) { - this.abc.unshiftObject(obj); + replace(idx, len, values) { + this.abc.splice(idx, len, ...values); + this.abc = [...this.abc]; } +} +``` + +#### `reverseObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - unshiftObjects(objs) { - this.abc.unshiftObjects(objs); + reverseObjects() { + this.abc.reverseObjects(); } } ``` @@ -539,97 +1038,107 @@ import { action } from '@ember/object'; import { TrackedArray } from 'tracked-built-ins'; export default class SampleComponent extends Component { - abc = new TrackedArray(); - - @action - addObject(value) { - if (!this.abc.includes(value)) { - this.abc.push(value); - } - } - - @action - addObjects(values) { - values.forEach(v => this.addObject(v)) - } + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - clear() { - this.abc.splice(0, this.abc.length); + reverseObjects() { + this.abc.reverse(); } +} +``` +or - @action - insertAt(idx, value) { - this.abc.splice(idx, 0, value); - } +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; - @action - popObject() { - this.abc.pop(); - } +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; @action - pushAction(value) { - this.abc.push(value); + reverseObjects() { + this.abc = [...this.abc.reverse()]; } +} +``` - @action - pushObjects(values) { - this.abc.splice(this.abc.length, 0, ...values); - } +#### `setObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - removeAt(start, len) { - this.abc.splice(start, len); + setObjects(values) { + this.abc.setObjects(values); } +} +``` - @action - removeObject(value) { - let loc = this.abc.length || 0; - while (--loc >= 0) { - let curValue = this.abc.at(loc); +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; - if (curValue === value) { - this.abc.splice(loc, 1); - } - } - } +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - removeObjects(values) { - values.forEach(v => { - this.removeObject(v); - }) + setObjects(values) { + this.abc.splice(0, this.abc.length, ...values); } +} +``` +or - @action - replace(idx, len, values) { - this.abc.splice(idx, len, ...values); - } +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; - @action - reverseObjects() { - this.abc.reverse(); - } +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; @action setObjects(values) { - this.abc.splice(0, this.abc.length, ...values); + this.abc = [...values]; } +} +``` + +#### `shiftObject` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action shiftObject() { - this.abc.shift(); + this.abc.shiftObject(); } +} +``` - @action - unshiftObject(obj) { - this.abc.unshift(obj); - } +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - unshiftObjects(objs) { - this.abc.unshift(...objs); + shiftObject() { + this.abc.shift(); } } ``` @@ -642,106 +1151,114 @@ import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; export default class SampleComponent extends Component { - @tracked abc = []; + @tracked abc = ['x', 'y', 'z', 'x']; @action - addObject(value) { - if (!this.abc.includes(value)) { - [...this.abc, value]; - } + shiftObject() { + this.abc.shift(); + this.abc = [...this.abc] } +} +``` - @action - addObjects(values) { - values.forEach(v => this.addObject(v)) - } +#### `unshiftObject` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - clear() { - this.abc = []; + unshiftObject(obj) { + this.abc.unshiftObject(obj); } +} +``` - @action - insertAt(idx, value) { - this.abc = [...this.abc.slice(0, idx), value, this.abc.slice(this.abc.length - idx)] - } +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; - @action - popObject() { - this.abc.pop(); - this.abc = [...this.abc]; - } +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - pushAction(value) { - this.abc = [...this.abc, value]; + unshiftObject(obj) { + this.abc.unshift(obj); } +} +``` +or - @action - pushObjects(values) { - this.abc = [...this.abc, ...values]; - } +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; - @action - removeAt(start, len) { - this.abc.splice(start, len); - this.abc = [...this.abc]; - } +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; @action - removeObject(value) { - let loc = this.abc.length || 0; - while (--loc >= 0) { - let curValue = this.abc.at(loc); - - if (curValue === value) { - this.abc.splice(loc, 1); - } - } + unshiftObject(obj) { + this.abc.unshift(obj); this.abc = [...this.abc]; } +} +``` - @action - removeObjects(values) { - values.forEach(v => { - this.removeObject(v); - }) - this.abc = [...this.abc]; - } +#### `unshiftObjects` +Before +```js +import Component from '@glimmer/component'; +export default class SampleComponent extends Component { + abc = ['x', 'y', 'z', 'x']; @action - replace(idx, len, values) { - this.abc.splice(idx, len, ...values); - this.abc = [...this.abc]; + unshiftObjects(objs) { + this.abc.unshiftObjects(objs); } +``` - @action - reverseObjects() { - this.abc = [...this.abc.reverse()]; - } +After +```js +// TrackedArray +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { TrackedArray } from 'tracked-built-ins'; + +export default class SampleComponent extends Component { + abc = new TrackedArray(['x', 'y', 'z', 'x']); @action - setObjects(values) { - this.abc = [...values]; + unshiftObjects(objs) { + this.abc.unshift(...objs); } +} +``` +or - @action - shiftObject() { - this.abc = [...this.abc.shift()]; - } +```js +// @tracked +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; - @action - unshiftObject(obj) { - this.abc = [...this.abc.unshift(obj)]; - } +export default class SampleComponent extends Component { + @tracked abc = ['x', 'y', 'z', 'x']; @action unshiftObjects(objs) { - this.abc = [...this.abc.unshift(...objs)]; + this.abc.unshift(...objs) + this.abc = [...this.abc]; } } ``` + It's always recommended to reference the existing implementation of the method you are trying to convert. This can make sure functionalities are kept as it was. Implementation details can be found in [`MutableArray`](https://api.emberjs.com/ember/release/classes/MutableArray), for example [`removeObject`](https://github.com/emberjs/ember.js/blob/v4.3.0/packages/%40ember/-internals/runtime/lib/mixins/array.js#L1619). From 3b7d96c94a1455432382132a7a7d4c7bd7e4e50b Mon Sep 17 00:00:00 2001 From: Hang Li Date: Thu, 30 Mar 2023 17:39:47 -0700 Subject: [PATCH 08/22] Update content/ember/v5/deprecate-array-prototype-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomek Nieżurawski --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index c686941d..c6e895e4 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -37,7 +37,7 @@ someArray.compact(); After: ```js -someArray.filter(val => val !=== undefined && val !== null); +someArray.filter(val => val !== undefined && val !== null); ``` #### `filterBy` From 6040e0fd87a08b118b13cd423072ac52b4106e41 Mon Sep 17 00:00:00 2001 From: Hang Li Date: Thu, 30 Mar 2023 17:40:01 -0700 Subject: [PATCH 09/22] Update content/ember/v5/deprecate-array-prototype-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomek Nieżurawski --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index c6e895e4..b7e620d0 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -63,7 +63,7 @@ someArray.findBy('isFruit'); // { food: 'apple', isFruit: true } After: ```js const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; -someArray.find(el => e.isFruit); // { food: 'apple', isFruit: true } +someArray.find(el => el.isFruit); // { food: 'apple', isFruit: true } ``` #### `getEach` From 9fe50d1293bb3e8cf471a902b8f06a7c00a6c426 Mon Sep 17 00:00:00 2001 From: Hang Li Date: Thu, 30 Mar 2023 17:40:14 -0700 Subject: [PATCH 10/22] Update content/ember/v5/deprecate-array-prototype-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomek Nieżurawski --- content/ember/v5/deprecate-array-prototype-extensions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index b7e620d0..d926a322 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -222,8 +222,8 @@ After: ```js const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; [...someArray].sort((a, b) => { - return a.food?.localCompare(b.food) - ? a.food?.localCompare(b.food) + return a.food?.localeCompare(b.food) + ? a.food?.localeCompare(b.food) : a.isFruit - b.isFruit; }); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }] ``` From 814dfca2599a4c253e8074eba39445c7169e997e Mon Sep 17 00:00:00 2001 From: Hang Li Date: Thu, 30 Mar 2023 17:41:04 -0700 Subject: [PATCH 11/22] Update content/ember/v5/deprecate-array-prototype-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomek Nieżurawski --- content/ember/v5/deprecate-array-prototype-extensions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index d926a322..3692fce0 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -251,8 +251,8 @@ someArray.uniq(); // [1, 2, 3, undefined] After: ```js -const someArray = [1, 2, 3, undefined, 3]; -[...new Set(someArray)] // [1, 2, 3, undefined] +const someArray = ['a', 'a', 'b', 'b'] +[...new Set(someArray)] // ['a', 'b'] ``` #### `uniqBy` From 4396fc16a9b92e28fd99f1912c188b1560bc22da Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 14 Jun 2024 14:56:36 -0400 Subject: [PATCH 12/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Simon Ihmig --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 3692fce0..aca72caf 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -2,7 +2,7 @@ id: deprecate-array-prototype-extensions title: "Deprecate array prototype extensions" until: '6.0.0' -since: '5.0.0' +since: '5.10.0' --- Ember historically extended the prototypes of native Javascript arrays to implement `Ember.Enumerable`, `Ember.MutableEnumerable`, `Ember.MutableArray`, `Ember.Array`. As of v5, the usages of array prototype extensions are deprecated. From 5572a981bd914e50498d55cadd5aa9b1bbb15725 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 14 Jun 2024 15:01:02 -0400 Subject: [PATCH 13/22] Update content/ember/v5/deprecate-array-prototype-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomek Nieżurawski --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index aca72caf..60db62b5 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -125,7 +125,7 @@ someArray.isAny('isFruit'); // true After: ```js const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]; -someArray.any(el => el.isFruit); // true +someArray.some(el => el.isFruit); // true ``` #### `isEvery` From 4bb9fb3921af0e90d5f616a7e90f1d49439698be Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 28 Jun 2024 14:56:10 -0400 Subject: [PATCH 14/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Jared Galanis --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 60db62b5..6ee7dd21 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -349,7 +349,7 @@ items.forEach(item => { If the `firstObject` and `lastObject` are used in a template, you can convert to use `get` helper safely as `get` helper handles the reactivity already. Before -```hbs +```handlebars ``` From e8d01b421588f209c7352a055ec50414062aea93 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 28 Jun 2024 14:56:21 -0400 Subject: [PATCH 15/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Jared Galanis --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 6ee7dd21..86d32a48 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -354,7 +354,7 @@ Before ``` After -```hbs +```handlebars ``` From 7e969157a70d19a3cd609b02efcccbef3e922b92 Mon Sep 17 00:00:00 2001 From: Ignace Maes Date: Sun, 7 Jul 2024 20:47:58 +0200 Subject: [PATCH 16/22] deps: upgrade ember-showdown-shiki --- package-lock.json | 118 ++++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56218c60..e3e074da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "ember-qunit": "^5.1.5", "ember-resolver": "^8.0.3", "ember-set-body-class": "^1.0.2", - "ember-showdown-shiki": "^1.1.0", + "ember-showdown-shiki": "^1.2.1", "ember-source": "~4.4.0", "ember-styleguide": "^7.1.0", "ember-template-lint": "^5.7.1", @@ -4811,18 +4811,30 @@ } }, "node_modules/@shikijs/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.0.tgz", - "integrity": "sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==", - "dev": true + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.3.tgz", + "integrity": "sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/core/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } }, "node_modules/@shikijs/transformers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.2.0.tgz", - "integrity": "sha512-xKn7DtA65DQV4FOfYsrvqM80xOy2xuXnxWWKsZmHv1VII/IOuDUDsWDu3KnpeLH6wqNJWp1GRoNUsHR1aw/VhQ==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.10.3.tgz", + "integrity": "sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==", "dev": true, "dependencies": { - "shiki": "1.2.0" + "shiki": "1.10.3" } }, "node_modules/@simple-dom/document": { @@ -18505,15 +18517,15 @@ } }, "node_modules/ember-showdown-shiki": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ember-showdown-shiki/-/ember-showdown-shiki-1.1.0.tgz", - "integrity": "sha512-sL3ExL3w+buHNfDmZywhqzxwsP53MZA0BH7eo0aHOmgVIhJkzxZL3jJtut1biIey/XzRfoVp7b66aEQSGcYlhg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ember-showdown-shiki/-/ember-showdown-shiki-1.2.1.tgz", + "integrity": "sha512-h3WYvEVjK7R86SosU6Y9lA7kvlVff4Y/7KS/D79lJ828NReLW1CnQAcxRSoGVvu+S6A8uONzImzJhyDXwuZDZA==", "dev": true, "dependencies": { "@embroider/addon-shim": "^1.8.7", - "@shikijs/transformers": "^1.2.0", + "@shikijs/transformers": "^1.9.1", "decorator-transforms": "^1.0.1", - "shiki": "^1.2.0" + "shiki": "^1.9.1" }, "peerDependencies": { "showdown": ">1.0.0" @@ -31997,12 +32009,22 @@ "dev": true }, "node_modules/shiki": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.0.tgz", - "integrity": "sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.3.tgz", + "integrity": "sha512-eneCLncGuvPdTutJuLyUGS8QNPAVFO5Trvld2wgEq1e002mwctAhJKeMGWtWVXOIEzmlcLRqcgPSorR6AVzOmQ==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.10.3", + "@types/hast": "^3.0.4" + } + }, + "node_modules/shiki/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dev": true, "dependencies": { - "@shikijs/core": "1.2.0" + "@types/unist": "*" } }, "node_modules/showdown": { @@ -40460,18 +40482,32 @@ "dev": true }, "@shikijs/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.0.tgz", - "integrity": "sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==", - "dev": true + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.3.tgz", + "integrity": "sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==", + "dev": true, + "requires": { + "@types/hast": "^3.0.4" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + } + } }, "@shikijs/transformers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.2.0.tgz", - "integrity": "sha512-xKn7DtA65DQV4FOfYsrvqM80xOy2xuXnxWWKsZmHv1VII/IOuDUDsWDu3KnpeLH6wqNJWp1GRoNUsHR1aw/VhQ==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.10.3.tgz", + "integrity": "sha512-MNjsyye2WHVdxfZUSr5frS97sLGe6G1T+1P41QjyBFJehZphMcr4aBlRLmq6OSPBslYe9byQPVvt/LJCOfxw8Q==", "dev": true, "requires": { - "shiki": "1.2.0" + "shiki": "1.10.3" } }, "@simple-dom/document": { @@ -52019,15 +52055,15 @@ } }, "ember-showdown-shiki": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ember-showdown-shiki/-/ember-showdown-shiki-1.1.0.tgz", - "integrity": "sha512-sL3ExL3w+buHNfDmZywhqzxwsP53MZA0BH7eo0aHOmgVIhJkzxZL3jJtut1biIey/XzRfoVp7b66aEQSGcYlhg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ember-showdown-shiki/-/ember-showdown-shiki-1.2.1.tgz", + "integrity": "sha512-h3WYvEVjK7R86SosU6Y9lA7kvlVff4Y/7KS/D79lJ828NReLW1CnQAcxRSoGVvu+S6A8uONzImzJhyDXwuZDZA==", "dev": true, "requires": { "@embroider/addon-shim": "^1.8.7", - "@shikijs/transformers": "^1.2.0", + "@shikijs/transformers": "^1.9.1", "decorator-transforms": "^1.0.1", - "shiki": "^1.2.0" + "shiki": "^1.9.1" } }, "ember-source": { @@ -62761,12 +62797,24 @@ "dev": true }, "shiki": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.0.tgz", - "integrity": "sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.3.tgz", + "integrity": "sha512-eneCLncGuvPdTutJuLyUGS8QNPAVFO5Trvld2wgEq1e002mwctAhJKeMGWtWVXOIEzmlcLRqcgPSorR6AVzOmQ==", "dev": true, "requires": { - "@shikijs/core": "1.2.0" + "@shikijs/core": "1.10.3", + "@types/hast": "^3.0.4" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + } } }, "showdown": { diff --git a/package.json b/package.json index 0a1d4c6f..5e1518a9 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "ember-qunit": "^5.1.5", "ember-resolver": "^8.0.3", "ember-set-body-class": "^1.0.2", - "ember-showdown-shiki": "^1.1.0", + "ember-showdown-shiki": "^1.2.1", "ember-source": "~4.4.0", "ember-styleguide": "^7.1.0", "ember-template-lint": "^5.7.1", From 765a3a8f03138f9826feaf817d9ec6d016933e67 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:28:30 -0400 Subject: [PATCH 17/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Simon Ihmig --- content/ember/v5/deprecate-array-prototype-extensions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 86d32a48..6dc1812a 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -251,8 +251,8 @@ someArray.uniq(); // [1, 2, 3, undefined] After: ```js -const someArray = ['a', 'a', 'b', 'b'] -[...new Set(someArray)] // ['a', 'b'] +const someArray = [1, 2, 3, undefined, 3]; +[...new Set(someArray)] // [1, 2, 3, undefined] ``` #### `uniqBy` From 4f991c2c0dd0c46184a314b2dcc238f66abfa387 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:28:57 -0400 Subject: [PATCH 18/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Simon Ihmig --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 6dc1812a..b15aaf2e 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -389,7 +389,7 @@ import { action } from '@ember/object'; import { TrackedArray } from 'tracked-built-ins'; export default class SampleComponent extends Component { - abc = new TrackedArray(); + abc = new TrackedArray(['x', 'y', 'z', 'x']); get lastObj() { return this.abc.at(-1); From 8185fd536015f3b13dd0b7aafac297832f89b52d Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:30:37 -0400 Subject: [PATCH 19/22] Update content/ember/v5/deprecate-array-prototype-extensions.md --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index b15aaf2e..b3501e17 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -758,7 +758,7 @@ export default class SampleComponent extends Component { @action pushObjects(values) { - this.abc.splice(this.abc.length, 0, ...values); + this.abc.push(...values); } } ``` From a14cce9d20a1515f42f90ae9f205beb6714e23fa Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:31:14 -0400 Subject: [PATCH 20/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Simon Ihmig --- content/ember/v5/deprecate-array-prototype-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index b3501e17..5b161318 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -824,7 +824,7 @@ export default class SampleComponent extends Component { @action removeAt(start, len) { this.abc.splice(start, len); - this.abc = [...this.abc]; + this.abc = this.abc; } } ``` From d5625389719c443b70eac4d2fe8a7c329c637cf0 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:31:51 -0400 Subject: [PATCH 21/22] Update content/ember/v5/deprecate-array-prototype-extensions.md Co-authored-by: Simon Ihmig --- content/ember/v5/deprecate-array-prototype-extensions.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index 5b161318..f2f8544a 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -855,14 +855,7 @@ export default class SampleComponent extends Component { @action removeObject(value) { - let loc = this.abc.length || 0; - while (--loc >= 0) { - let curValue = this.abc.at(loc); - - if (curValue === value) { - this.abc.splice(loc, 1); - } - } + this.abc.filter(item => item !== value); } } ``` From 687fe10409e30a76fdb0bc04c788b459e9505b95 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Fri, 12 Jul 2024 14:42:32 -0400 Subject: [PATCH 22/22] Update deprecate-array-prototype-extensions.md --- content/ember/v5/deprecate-array-prototype-extensions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/ember/v5/deprecate-array-prototype-extensions.md b/content/ember/v5/deprecate-array-prototype-extensions.md index f2f8544a..ce993aa2 100644 --- a/content/ember/v5/deprecate-array-prototype-extensions.md +++ b/content/ember/v5/deprecate-array-prototype-extensions.md @@ -1,5 +1,4 @@ --- -id: deprecate-array-prototype-extensions title: "Deprecate array prototype extensions" until: '6.0.0' since: '5.10.0'