From 4fad71c72a2bb3f15d445232666feba96de8b6f1 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Thu, 24 Oct 2024 11:21:22 -0400 Subject: [PATCH 1/6] More progress removing prototype extensions --- app/controllers/application.js | 31 +++++++++++++++++-------------- app/controllers/deprecations.js | 4 +++- app/libs/promise-assembler.js | 12 +++++++----- app/routes/deprecations.js | 17 ++++++++--------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/app/controllers/application.js b/app/controllers/application.js index f96f748e2a..3fc10afd0b 100644 --- a/app/controllers/application.js +++ b/app/controllers/application.js @@ -5,6 +5,8 @@ import { equal } from '@ember/object/computed'; import { debounce, schedule } from '@ember/runloop'; import { inject as service } from '@ember/service'; +import { TrackedArray } from 'tracked-built-ins'; + export default class ApplicationController extends Controller { /** * Service used to broadcast changes to the application's layout @@ -49,7 +51,7 @@ export default class ApplicationController extends Controller { constructor() { super(...arguments); - this.mixinStack = []; + this.mixinStack = new TrackedArray([]); this.mixinDetails = []; } @@ -67,15 +69,14 @@ export default class ApplicationController extends Controller { errors, }; - this.mixinStack.pushObject(details); + this.mixinStack.push(details); this.set('mixinDetails', details); } @action popMixinDetails() { - let mixinStack = this.mixinStack; - let item = mixinStack.popObject(); - this.set('mixinDetails', mixinStack.get('lastObject')); + const item = this.mixinStack.pop(); + this.set('mixinDetails', this.mixinStack.at(-1)); this.port.send('objectInspector:releaseObject', { objectId: item.objectId, }); @@ -166,26 +167,28 @@ export default class ApplicationController extends Controller { }); }); - this.set('mixinStack', []); + this.set('mixinStack', new TrackedArray([])); this.pushMixinDetails(name, undefined, objectId, details, errors); } @action droppedObject(objectId) { - let mixinStack = this.mixinStack; - let obj = mixinStack.find((x) => x.objectId === objectId); + let obj = this.mixinStack.find((mixin) => mixin.objectId === objectId); if (obj) { - let index = mixinStack.indexOf(obj); - let objectsToRemove = []; + let index = this.mixinStack.indexOf(obj); + let objectsToRemove = new TrackedArray([]); for (let i = index; i >= 0; i--) { - objectsToRemove.pushObject(mixinStack.objectAt(i)); + objectsToRemove.push(this.mixinStack.at(i)); } objectsToRemove.forEach((item) => { - mixinStack.removeObject(item); + const index = this.mixinStack.indexOf(item); + if (index !== -1) { + this.mixinStack.splice(index, 1); + } }); } - if (mixinStack.get('length') > 0) { - this.set('mixinDetails', mixinStack.get('lastObject')); + if (this.mixinStack.get('length') > 0) { + this.set('mixinDetails', this.mixinStack.at(-1)); } else { this.set('mixinDetails', null); } diff --git a/app/controllers/deprecations.js b/app/controllers/deprecations.js index 8c42b1da72..4af7337f23 100644 --- a/app/controllers/deprecations.js +++ b/app/controllers/deprecations.js @@ -4,6 +4,8 @@ import Controller from '@ember/controller'; import debounceComputed from 'ember-inspector/computed/debounce'; import searchMatch from 'ember-inspector/utils/search-match'; +import { TrackedArray } from 'tracked-built-ins'; + export default class DeprecationsController extends Controller { @service adapter; @service port; @@ -15,7 +17,7 @@ export default class DeprecationsController extends Controller { constructor() { super(...arguments); - set(this, 'deprecations', []); + set(this, 'deprecations', new TrackedArray([])); } @action diff --git a/app/libs/promise-assembler.js b/app/libs/promise-assembler.js index ea5ad4efca..3fe3870818 100644 --- a/app/libs/promise-assembler.js +++ b/app/libs/promise-assembler.js @@ -4,6 +4,8 @@ import EmberObject from '@ember/object'; import EventedMixin from '@ember/object/evented'; import Promise from 'ember-inspector/models/promise'; +import { TrackedArray } from 'tracked-built-ins'; + export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { // Used to track whether current message received // is the first in the request @@ -13,8 +15,8 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { init() { super.init(...arguments); - this.all = []; - this.topSort = []; + this.all = new TrackedArray([]); + this.topSort = new TrackedArray([]); this.topSortMeta = {}; this.promiseIndex = {}; } @@ -121,7 +123,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { let parentIndex = topSort.indexOf(promise.get('parent')); topSort.insertAt(parentIndex + 1, promise); } else { - topSort.pushObject(promise); + this.topSort.push(promise); } promise.get('children').forEach((child) => { topSort.removeObject(child); @@ -144,7 +146,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { let promise = Promise.create(props); let index = this.get('all.length'); - this.all.pushObject(promise); + this.all.push(promise); this.promiseIndex[promise.get('guid')] = index; return promise; } @@ -153,7 +155,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { if (guid) { let index = this.promiseIndex[guid]; if (index !== undefined) { - return this.all.objectAt(index); + return this.all.at(index); } } else { return this.all; diff --git a/app/routes/deprecations.js b/app/routes/deprecations.js index f3d082d83b..478381ed53 100644 --- a/app/routes/deprecations.js +++ b/app/routes/deprecations.js @@ -37,25 +37,24 @@ export default class DeprecationsRoute extends TabRoute { } deprecationsAdded(message) { - // eslint-disable-next-line ember/no-controller-access-in-routes - let { deprecations } = this.controller; - message.deprecations.forEach((item) => { - let record = deprecations.find((x) => x.id === item.id); + let record = this.controller.deprecations.find( + (deprecation) => deprecation.id === item.id, + ); + if (record) { setProperties(record, item); } else { - deprecations.pushObject(item); + // eslint-disable-next-line ember/no-controller-access-in-routes + this.controller.deprecations.push(item); } }); } @action clear() { - // eslint-disable-next-line ember/no-controller-access-in-routes - let { deprecations } = this.controller; - this.port.send('deprecation:clear'); - deprecations.clear(); + // eslint-disable-next-line ember/no-controller-access-in-routes + this.controller.deprecations.splice(0, this.controller.deprecations.length); } } From 0ea09792377703254bb2b5b3acbd9d7f0f019968 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Thu, 24 Oct 2024 15:04:38 -0400 Subject: [PATCH 2/6] Fix a test --- app/controllers/deprecations.js | 27 +++++++++++++++------------ app/libs/promise-assembler.js | 28 ++++++++++++++++++---------- app/models/promise.js | 14 +++++++------- app/routes/deprecations.js | 7 ------- app/templates/deprecations.hbs | 3 +-- 5 files changed, 41 insertions(+), 38 deletions(-) diff --git a/app/controllers/deprecations.js b/app/controllers/deprecations.js index 4af7337f23..be370532fc 100644 --- a/app/controllers/deprecations.js +++ b/app/controllers/deprecations.js @@ -1,41 +1,44 @@ -import { action, computed, set } from '@ember/object'; +import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; -import debounceComputed from 'ember-inspector/computed/debounce'; -import searchMatch from 'ember-inspector/utils/search-match'; +import { tracked } from '@glimmer/tracking'; import { TrackedArray } from 'tracked-built-ins'; +import debounceComputed from 'ember-inspector/computed/debounce'; +import searchMatch from 'ember-inspector/utils/search-match'; + export default class DeprecationsController extends Controller { @service adapter; @service port; - search = null; - toggleDeprecationWorkflow = false; + deprecations = new TrackedArray([]); + @tracked search = null; + @tracked toggleDeprecationWorkflow = false; @debounceComputed('search', 300) searchValue; - constructor() { - super(...arguments); - set(this, 'deprecations', new TrackedArray([])); - } - @action changeDeprecationWorkflow(e) { - this.set('toggleDeprecationWorkflow', e.target.checked); + this.toggleDeprecationWorkflow = e.target.checked; this.port.send('deprecation:setOptions', { options: { toggleDeprecationWorkflow: this.toggleDeprecationWorkflow }, }); } - @computed('deprecations.@each.message', 'search') get filtered() { return this.deprecations.filter((item) => searchMatch(item.message, this.search), ); } + @action + clear() { + this.port.send('deprecation:clear'); + this.deprecations.splice(0, this.deprecations.length); + } + @action openResource(item) { this.adapter.openResource(item.fullSource, item.line); diff --git a/app/libs/promise-assembler.js b/app/libs/promise-assembler.js index 3fe3870818..870e2fef8d 100644 --- a/app/libs/promise-assembler.js +++ b/app/libs/promise-assembler.js @@ -5,18 +5,20 @@ import EventedMixin from '@ember/object/evented'; import Promise from 'ember-inspector/models/promise'; import { TrackedArray } from 'tracked-built-ins'; +import { tracked } from '@glimmer/tracking'; export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { // Used to track whether current message received // is the first in the request // Mainly helps in triggering 'firstMessageReceived' event - firstMessageReceived = false; + @tracked firstMessageReceived = false; + + all = new TrackedArray([]); + topSort = new TrackedArray([]); init() { super.init(...arguments); - this.all = new TrackedArray([]); - this.topSort = new TrackedArray([]); this.topSortMeta = {}; this.promiseIndex = {}; } @@ -35,9 +37,9 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { reset() { this.set('topSortMeta', {}); this.set('promiseIndex', {}); - this.topSort.clear(); + this.topSort.splice(0, this.topSort.length); - this.set('firstMessageReceived', false); + this.firstMessageReceived = false; let all = this.all; // Lazily destroy promises // Allows for a smooth transition on deactivate, @@ -49,7 +51,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { }, 500, ); - this.set('all', []); + this.set('all', new TrackedArray([])); } destroyPromises(promises) { @@ -62,7 +64,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { this.rebuildPromises(message.promises); if (!this.firstMessageReceived) { - this.set('firstMessageReceived', true); + this.firstMessageReceived = true; this.trigger('firstMessageReceived'); } } @@ -108,7 +110,10 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { } if (!isNew && hasParent !== hadParent) { // todo: implement recursion to reposition children - topSort.removeObject(promise); + const index = topSort.indexOf(promise); + if (index !== -1) { + topSort.splice(index, 1); + } parentChanged = true; } meta.hasParent = hasParent; @@ -121,12 +126,15 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { let topSort = this.topSort; if (promise.get('parent')) { let parentIndex = topSort.indexOf(promise.get('parent')); - topSort.insertAt(parentIndex + 1, promise); + topSort.splice(parentIndex + 1, 0, promise); } else { this.topSort.push(promise); } promise.get('children').forEach((child) => { - topSort.removeObject(child); + const index = topSort.indexOf(child); + if (index !== -1) { + topSort.splice(index, 1); + } this.insertInTopSort(child); }); } diff --git a/app/models/promise.js b/app/models/promise.js index ce47edc34c..84a871ae8b 100644 --- a/app/models/promise.js +++ b/app/models/promise.js @@ -5,6 +5,7 @@ import { typeOf, isEmpty } from '@ember/utils'; // eslint-disable-next-line ember/no-observers import EmberObject, { computed } from '@ember/object'; import escapeRegExp from 'ember-inspector/utils/escape-reg-exp'; +import { tracked } from '@glimmer/tracking'; const dateComputed = function () { return computed({ @@ -29,6 +30,7 @@ export default class Promise extends EmberObject { @dateComputed() settledAt; + @tracked branchLabel = ''; parent = null; @computed('parent.level') @@ -70,11 +72,11 @@ export default class Promise extends EmberObject { } recursiveState(prop, cp) { - if (this.get(prop)) { + if (this[prop]) { return true; } - for (let i = 0; i < this.get('children.length'); i++) { - if (this.children.at(i).get(cp)) { + for (let i = 0; i < this.children.length; i++) { + if (this.children.at(i)[cp]) { return true; } } @@ -111,9 +113,9 @@ export default class Promise extends EmberObject { return; } if (replace) { - this.set('branchLabel', label); + this.branchLabel = label; } else { - this.set('branchLabel', `${this.branchLabel} ${label}`); + this.branchLabel = `${this.branchLabel} ${label}`; } let parent = this.parent; @@ -122,8 +124,6 @@ export default class Promise extends EmberObject { } } - branchLabel = ''; - matches(val) { return !!this.branchLabel .toLowerCase() diff --git a/app/routes/deprecations.js b/app/routes/deprecations.js index 478381ed53..994c731cd3 100644 --- a/app/routes/deprecations.js +++ b/app/routes/deprecations.js @@ -50,11 +50,4 @@ export default class DeprecationsRoute extends TabRoute { } }); } - - @action - clear() { - this.port.send('deprecation:clear'); - // eslint-disable-next-line ember/no-controller-access-in-routes - this.controller.deprecations.splice(0, this.controller.deprecations.length); - } } diff --git a/app/templates/deprecations.hbs b/app/templates/deprecations.hbs index 5acf6ef60e..d3e48bb073 100644 --- a/app/templates/deprecations.hbs +++ b/app/templates/deprecations.hbs @@ -1,9 +1,8 @@ -{{! template-lint-disable no-action}} {{#if this.toolbarContainer}} {{#in-element this.toolbarContainer}} From 4dc7542b9b885acfb1a6ccd53f737af2f5cc5e0b Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Fri, 25 Oct 2024 12:57:08 -0400 Subject: [PATCH 3/6] Update deprecations.js --- app/routes/deprecations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/deprecations.js b/app/routes/deprecations.js index 994c731cd3..841a75715e 100644 --- a/app/routes/deprecations.js +++ b/app/routes/deprecations.js @@ -1,6 +1,6 @@ import { inject as service } from '@ember/service'; import { Promise } from 'rsvp'; -import { action, setProperties } from '@ember/object'; +import { setProperties } from '@ember/object'; import TabRoute from 'ember-inspector/routes/tab'; export default class DeprecationsRoute extends TabRoute { From 068d8f9681e7f9351544a20d9bda87db7abb7159 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Fri, 25 Oct 2024 13:13:08 -0400 Subject: [PATCH 4/6] Modernize promise-tree --- app/controllers/promise-tree.js | 101 +++++++++++++++++++------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/app/controllers/promise-tree.js b/app/controllers/promise-tree.js index 1d94453d3e..d98479e81d 100644 --- a/app/controllers/promise-tree.js +++ b/app/controllers/promise-tree.js @@ -1,31 +1,44 @@ -// eslint-disable-next-line ember/no-observers -import { action, observer } from '@ember/object'; +import { action } from '@ember/object'; import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; -import { equal, bool, and, not, filter } from '@ember/object/computed'; +import { filter } from '@ember/object/computed'; import { debounce, next, once } from '@ember/runloop'; +import { tracked } from '@glimmer/tracking'; + +// eslint-disable-next-line ember/no-observers +import { observes } from '@ember-decorators/object'; -export default Controller.extend({ - queryParams: ['filter'], +export default class PromiseTreeController extends Controller { + queryParams = ['filter']; - adapter: service(), - port: service(), + @service adapter; + @service port; - createdAfter: null, + @tracked createdAfter = null; + @tracked filter = 'all'; + @tracked searchValue = null; + @tracked effectiveSearch = null; // below used to show the "refresh" message - isEmpty: equal('model.length', 0), - wasCleared: bool('createdAfter'), - neverCleared: not('wasCleared'), - shouldRefresh: and('isEmpty', 'neverCleared'), + get isEmpty() { + return this.model.length === 0; + } + get wasCleared() { + return !this.createdAfter; + } + get neverCleared() { + return !this.wasCleared; + } + get shouldRefresh() { + return this.isEmpty && this.neverCleared; + } // Keep track of promise stack traces. // It is opt-in due to performance reasons. - instrumentWithStack: false, + @tracked instrumentWithStack = false; - /* jscs:disable validateIndentation */ - filtered: filter( + filtered = filter( 'model.@each.{createdAt,fulfilledBranch,rejectedBranch,pendingBranch,isVisible}', function (item) { // exclude cleared promises @@ -61,26 +74,24 @@ export default Controller.extend({ } return true; }, - ), - /* jscs:enable validateIndentation */ - - filter: 'all', - searchValue: null, - effectiveSearch: null, + ); // eslint-disable-next-line ember/no-observers - searchChanged: observer('searchValue', function () { + @observes('searchValue') + searchChanged() { debounce(this, this.notifyChange, 500); - }), + } + @action notifyChange() { - this.set('effectiveSearch', this.searchValue); + this.effectiveSearch = this.searchValue; next(() => { this.notifyPropertyChange('model'); }); - }, + } - toggleExpand: action(function (promise) { + @action + toggleExpand(promise) { let isExpanded = !promise.get('isExpanded'); promise.set('isManuallyExpanded', isExpanded); promise.recalculateExpanded(); @@ -94,37 +105,43 @@ export default Controller.extend({ } }); } - }), + } - tracePromise: action(function (promise) { + @action + tracePromise(promise) { this.port.send('promise:tracePromise', { promiseId: promise.get('guid') }); - }), + } - inspectObject: action(function () { + @action + inspectObject() { this.target.send('inspectObject', ...arguments); - }), + } - sendValueToConsole: action(function (promise) { + @action + sendValueToConsole(promise) { this.port.send('promise:sendValueToConsole', { promiseId: promise.get('guid'), }); - }), + } - setFilter: action(function (filter) { - this.set('filter', filter); + @action + setFilter(filter) { + this.filter = filter; next(() => { this.notifyPropertyChange('filtered'); }); - }), + } - updateInstrumentWithStack: action(function (bool) { + @action + updateInstrumentWithStack(bool) { this.port.send('promise:setInstrumentWithStack', { instrumentWithStack: bool, }); - }), + } - clear: action(function () { - this.set('createdAfter', new Date()); + @action + clear() { + this.createdAfter = new Date(); once(this, this.notifyChange); - }), -}); + } +} From 8109162f3f2b39e451a9f817abc8df9f00f88759 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Fri, 25 Oct 2024 15:32:54 -0400 Subject: [PATCH 5/6] Remove some more computeds --- app/controllers/promise-tree.js | 10 ++++------ app/models/promise.js | 31 ++++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/controllers/promise-tree.js b/app/controllers/promise-tree.js index d98479e81d..6c57beebce 100644 --- a/app/controllers/promise-tree.js +++ b/app/controllers/promise-tree.js @@ -2,7 +2,6 @@ import { action } from '@ember/object'; import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { isEmpty } from '@ember/utils'; -import { filter } from '@ember/object/computed'; import { debounce, next, once } from '@ember/runloop'; import { tracked } from '@glimmer/tracking'; @@ -38,9 +37,8 @@ export default class PromiseTreeController extends Controller { // It is opt-in due to performance reasons. @tracked instrumentWithStack = false; - filtered = filter( - 'model.@each.{createdAt,fulfilledBranch,rejectedBranch,pendingBranch,isVisible}', - function (item) { + get filtered() { + return this.model.filter((item) => { // exclude cleared promises if (this.createdAfter && item.get('createdAt') < this.createdAfter) { return false; @@ -73,8 +71,8 @@ export default class PromiseTreeController extends Controller { return item.matches(search); } return true; - }, - ); + }); + } // eslint-disable-next-line ember/no-observers @observes('searchValue') diff --git a/app/models/promise.js b/app/models/promise.js index 84a871ae8b..40680c1052 100644 --- a/app/models/promise.js +++ b/app/models/promise.js @@ -1,5 +1,4 @@ import { observes } from '@ember-decorators/object'; -import { or, equal, not } from '@ember/object/computed'; import { once } from '@ember/runloop'; import { typeOf, isEmpty } from '@ember/utils'; // eslint-disable-next-line ember/no-observers @@ -31,7 +30,9 @@ export default class Promise extends EmberObject { settledAt; @tracked branchLabel = ''; - parent = null; + @tracked isExpanded = false; + @tracked isManuallyExpanded = undefined; + @tracked parent = null; @computed('parent.level') get level() { @@ -42,17 +43,21 @@ export default class Promise extends EmberObject { return parent.get('level') + 1; } - @or('isFulfilled', 'isRejected') - isSettled; + get isSettled() { + return this.isFulfilled || this.isRejected; + } - @equal('state', 'fulfilled') - isFulfilled; + get isFulfilled() { + return this.state === 'fulfilled'; + } - @equal('state', 'rejected') - isRejected; + get isRejected() { + return this.state === 'rejected'; + } - @not('isSettled') - isPending; + get isPending() { + return !this.isSettled; + } children = []; @@ -138,10 +143,6 @@ export default class Promise extends EmberObject { // EXPANDED / COLLAPSED PROMISES - isExpanded = false; - - isManuallyExpanded = undefined; - // eslint-disable-next-line ember/no-observers @observes('isPending', 'isFulfilled', 'isRejected', 'parent') stateOrParentChanged() { @@ -187,7 +188,7 @@ export default class Promise extends EmberObject { this.parent.recalculateExpanded(); } } - this.set('isExpanded', isExpanded); + this.isExpanded = isExpanded; return isExpanded; } From 578c504b57453e8b5be73d1ac74ef20d37205f8b Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Thu, 31 Oct 2024 12:24:42 -0400 Subject: [PATCH 6/6] Update app/controllers/promise-tree.js --- app/controllers/promise-tree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/promise-tree.js b/app/controllers/promise-tree.js index 6c57beebce..b789861a22 100644 --- a/app/controllers/promise-tree.js +++ b/app/controllers/promise-tree.js @@ -24,7 +24,7 @@ export default class PromiseTreeController extends Controller { return this.model.length === 0; } get wasCleared() { - return !this.createdAfter; + return Boolean(this.createdAfter); } get neverCleared() { return !this.wasCleared;