From 7dda036dd68134227cfccf3ec74a6226ab960dd0 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Wed, 15 Jan 2025 09:40:43 -0500 Subject: [PATCH 1/3] Remove Evented from PromiseAssembler --- ...mise-assembler.js => promise-assembler.ts} | 115 ++++++++++++------ app/models/{promise.js => promise.ts} | 75 ++++++------ .../{promise-tree.js => promise-tree.ts} | 23 ++-- app/services/port.ts | 6 +- app/services/{storage.js => storage.ts} | 20 +-- app/services/storage/{local.js => local.ts} | 21 +--- app/services/storage/{memory.js => memory.ts} | 25 +--- app/utils/nullish.ts | 7 ++ tsconfig.json | 1 + 9 files changed, 156 insertions(+), 137 deletions(-) rename app/libs/{promise-assembler.js => promise-assembler.ts} (54%) rename app/models/{promise.js => promise.ts} (71%) rename app/routes/{promise-tree.js => promise-tree.ts} (69%) rename app/services/{storage.js => storage.ts} (83%) rename app/services/storage/{local.js => local.ts} (75%) rename app/services/storage/{memory.js => memory.ts} (67%) create mode 100644 app/utils/nullish.ts diff --git a/app/libs/promise-assembler.js b/app/libs/promise-assembler.ts similarity index 54% rename from app/libs/promise-assembler.js rename to app/libs/promise-assembler.ts index 870e2fef8d..106ee10fb5 100644 --- a/app/libs/promise-assembler.js +++ b/app/libs/promise-assembler.ts @@ -1,27 +1,36 @@ import { assert } from '@ember/debug'; import { later } from '@ember/runloop'; -import EmberObject from '@ember/object'; -import EventedMixin from '@ember/object/evented'; -import Promise from 'ember-inspector/models/promise'; +import EmberObject, { action, setProperties } from '@ember/object'; +import { addListener, removeListener, sendEvent } from '@ember/object/events'; +import type { AnyFn } from 'ember/-private/type-utils'; -import { TrackedArray } from 'tracked-built-ins'; +import { TrackedArray, TrackedObject } from 'tracked-built-ins'; import { tracked } from '@glimmer/tracking'; -export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { +import PromiseModel from '../models/promise'; +import type PortService from '../services/port'; + +interface SerializedPromise { + children?: Array; + guid: string; + label: string; + parent?: string; + reason: string; + state: string; + value: string; +} + +export default class PromiseAssembler extends EmberObject { + declare port: PortService; // Used to track whether current message received // is the first in the request // Mainly helps in triggering 'firstMessageReceived' event @tracked firstMessageReceived = false; - all = new TrackedArray([]); - topSort = new TrackedArray([]); - - init() { - super.init(...arguments); - - this.topSortMeta = {}; - this.promiseIndex = {}; - } + all = new TrackedArray([]); + promiseIndex = new TrackedObject>({}); + topSort = new TrackedArray([]); + topSortMeta = new TrackedObject>({}); start() { this.port.on('promise:promisesUpdated', this, this.addOrUpdatePromises); @@ -35,8 +44,10 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { } reset() { - this.set('topSortMeta', {}); - this.set('promiseIndex', {}); + this.topSortMeta = new TrackedObject< + Record + >({}); + this.promiseIndex = new TrackedObject>({}); this.topSort.splice(0, this.topSort.length); this.firstMessageReceived = false; @@ -54,13 +65,13 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { this.set('all', new TrackedArray([])); } - destroyPromises(promises) { + destroyPromises(promises: Array) { promises.forEach(function (item) { item.destroy(); }); } - addOrUpdatePromises(message) { + addOrUpdatePromises(message: { promises: Array }) { this.rebuildPromises(message.promises); if (!this.firstMessageReceived) { @@ -69,7 +80,7 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { } } - rebuildPromises(promises) { + rebuildPromises(promises: Array) { promises.forEach((props) => { props = Object.assign({}, props); let childrenIds = props.children; @@ -87,26 +98,26 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { return; } let child = this.updateOrCreate({ guid: childId, parent: promise }); - promise.get('children').pushObject(child); + promise.children.push(child); }); } }); } - updateTopSort(promise) { + updateTopSort(promise: PromiseModel) { let topSortMeta = this.topSortMeta; - let guid = promise.get('guid'); - let meta = topSortMeta[guid]; + let guid = promise.guid; + let meta = topSortMeta[guid] ?? {}; let isNew = !meta; let hadParent = false; - let hasParent = !!promise.get('parent'); + let hasParent = !!promise.parent; let topSort = this.topSort; let parentChanged = isNew; if (isNew) { meta = topSortMeta[guid] = {}; } else { - hadParent = meta.hasParent; + hadParent = Boolean(meta.hasParent); } if (!isNew && hasParent !== hadParent) { // todo: implement recursion to reposition children @@ -122,15 +133,15 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { } } - insertInTopSort(promise) { + insertInTopSort(promise: PromiseModel) { let topSort = this.topSort; - if (promise.get('parent')) { - let parentIndex = topSort.indexOf(promise.get('parent')); + if (promise.parent) { + let parentIndex = topSort.indexOf(promise.parent); topSort.splice(parentIndex + 1, 0, promise); } else { this.topSort.push(promise); } - promise.get('children').forEach((child) => { + promise.children.forEach((child) => { const index = topSort.indexOf(child); if (index !== -1) { topSort.splice(index, 1); @@ -139,29 +150,29 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { }); } - updateOrCreate(props) { + updateOrCreate(props: any) { let guid = props.guid; let promise = this.findOrCreate(guid); - promise.setProperties(props); + setProperties(promise, props); this.updateTopSort(promise); return promise; } - createPromise(props) { - let promise = Promise.create(props); - let index = this.get('all.length'); + createPromise(props: any): PromiseModel { + let promise = PromiseModel.create(props); + let index = this.all.length; this.all.push(promise); - this.promiseIndex[promise.get('guid')] = index; + this.promiseIndex[promise.guid as keyof object] = index; return promise; } - find(guid) { + find(guid?: string) { if (guid) { - let index = this.promiseIndex[guid]; + let index = this.promiseIndex[guid as keyof object]; if (index !== undefined) { return this.all.at(index); } @@ -170,10 +181,36 @@ export default class PromiseAssembler extends EmberObject.extend(EventedMixin) { } } - findOrCreate(guid) { + findOrCreate(guid?: string) { if (!guid) { assert('You have tried to findOrCreate without a guid'); } - return this.find(guid) || this.createPromise({ guid }); + return (this.find(guid) as PromiseModel) || this.createPromise({ guid }); + } + + // Manually implement Evented functionality, so we can move away from the mixin + + @action + on(eventName: string, target: unknown = this, method: AnyFn) { + addListener(this, eventName, target, method); + } + + @action + one(eventName: string, target: unknown = this, method: AnyFn) { + addListener(this, eventName, target, method, true); + } + + @action + off(eventName: string, target: unknown = this, method: AnyFn) { + try { + removeListener(this, eventName, target, method); + } catch (e) { + console.error(e); + } + } + + @action + trigger(eventName: string, ...args: Array) { + sendEvent(this, eventName, args); } } diff --git a/app/models/promise.js b/app/models/promise.ts similarity index 71% rename from app/models/promise.js rename to app/models/promise.ts index 40680c1052..22dceedd43 100644 --- a/app/models/promise.js +++ b/app/models/promise.ts @@ -1,17 +1,22 @@ +// @ts-expect-error This does not seem to be typed import { observes } from '@ember-decorators/object'; import { once } from '@ember/runloop'; -import { typeOf, isEmpty } from '@ember/utils'; +import { typeOf } 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'; +import { TrackedArray } from 'tracked-built-ins'; + +import escapeRegExp from '../utils/escape-reg-exp'; +import { isNullish } from '../utils/nullish'; + const dateComputed = function () { return computed({ get() { return null; }, - set(key, date) { + set(_key, date: Date | number | string) { if (typeOf(date) === 'date') { return date; } else if (typeof date === 'number' || typeof date === 'string') { @@ -22,25 +27,27 @@ const dateComputed = function () { }); }; -export default class Promise extends EmberObject { - @dateComputed() - createdAt; - - @dateComputed() - settledAt; +export default class PromiseModel extends EmberObject { + children = new TrackedArray([]); + declare label?: string; + declare guid: string; + declare state: string; + // @ts-expect-error TODO: figure out types for this + @dateComputed() createdAt; + // @ts-expect-error TODO: figure out types for this + @dateComputed() settledAt; @tracked branchLabel = ''; @tracked isExpanded = false; @tracked isManuallyExpanded = undefined; - @tracked parent = null; + @tracked parent: PromiseModel | null = null; - @computed('parent.level') - get level() { + get level(): number { let parent = this.parent; if (!parent) { return 0; } - return parent.get('level') + 1; + return parent.level + 1; } get isSettled() { @@ -59,29 +66,24 @@ export default class Promise extends EmberObject { return !this.isSettled; } - children = []; - - @computed('isPending', 'children.@each.pendingBranch') get pendingBranch() { return this.recursiveState('isPending', 'pendingBranch'); } - @computed('isRejected', 'children.@each.rejectedBranch') get rejectedBranch() { return this.recursiveState('isRejected', 'rejectedBranch'); } - @computed('isFulfilled', 'children.@each.fulfilledBranch') get fulfilledBranch() { return this.recursiveState('isFulfilled', 'fulfilledBranch'); } - recursiveState(prop, cp) { + recursiveState(prop: keyof PromiseModel, cp: keyof PromiseModel) { if (this[prop]) { return true; } for (let i = 0; i < this.children.length; i++) { - if (this.children.at(i)[cp]) { + if (this.children.at(i)?.[cp]) { return true; } } @@ -97,9 +99,9 @@ export default class Promise extends EmberObject { return; } if ( - (this.pendingBranch && !this.get('parent.pendingBranch')) || - (this.fulfilledBranch && !this.get('parent.fulfilledBranch')) || - (this.rejectedBranch && !this.get('parent.rejectedBranch')) + (this.pendingBranch && !this.parent.pendingBranch) || + (this.fulfilledBranch && !this.parent.fulfilledBranch) || + (this.rejectedBranch && !this.parent.rejectedBranch) ) { this.parent.notifyPropertyChange('fulfilledBranch'); this.parent.notifyPropertyChange('rejectedBranch'); @@ -113,8 +115,8 @@ export default class Promise extends EmberObject { this.addBranchLabel(this.label, true); } - addBranchLabel(label, replace) { - if (isEmpty(label)) { + addBranchLabel(label?: string, replace?: boolean) { + if (isNullish(label)) { return; } if (replace) { @@ -129,13 +131,13 @@ export default class Promise extends EmberObject { } } - matches(val) { + matches(val: string) { return !!this.branchLabel .toLowerCase() .match(new RegExp(`.*${escapeRegExp(val.toLowerCase())}.*`)); } - matchesExactly(val) { + matchesExactly(val: string) { return !!(this.label || '') .toLowerCase() .match(new RegExp(`.*${escapeRegExp(val.toLowerCase())}.*`)); @@ -152,7 +154,7 @@ export default class Promise extends EmberObject { } } - _findTopParent() { + _findTopParent(): PromiseModel { let parent = this.parent; if (!parent) { return this; @@ -167,12 +169,12 @@ export default class Promise extends EmberObject { isExpanded = this.isManuallyExpanded; } else { let children = this._allChildren(); - for (let i = 0, l = children.length; i < l; i++) { - let child = children[i]; - if (child.get('isRejected')) { + for (let i = 0; i < children.length; i++) { + let child = children[i] as PromiseModel; + if (child.isRejected) { isExpanded = true; } - if (child.get('isPending') && !child.get('parent.isPending')) { + if (child.isPending && !child.parent!.isPending) { isExpanded = true; } if (isExpanded) { @@ -184,7 +186,7 @@ export default class Promise extends EmberObject { parents.forEach((parent) => { parent.set('isExpanded', true); }); - } else if (this.get('parent.isExpanded')) { + } else if (this.parent?.isExpanded) { this.parent.recalculateExpanded(); } } @@ -192,10 +194,9 @@ export default class Promise extends EmberObject { return isExpanded; } - @computed('parent.{isExpanded,isVisible}', 'parent') - get isVisible() { + get isVisible(): boolean { if (this.parent) { - return this.get('parent.isExpanded') && this.get('parent.isVisible'); + return this.parent.isExpanded && this.parent.isVisible; } return true; } @@ -208,7 +209,7 @@ export default class Promise extends EmberObject { return children; } - _allParents() { + _allParents(): Array { let parent = this.parent; if (parent) { return [parent, ...parent._allParents()]; diff --git a/app/routes/promise-tree.js b/app/routes/promise-tree.ts similarity index 69% rename from app/routes/promise-tree.js rename to app/routes/promise-tree.ts index f8dfe2d99b..b497e1573b 100644 --- a/app/routes/promise-tree.js +++ b/app/routes/promise-tree.ts @@ -1,22 +1,31 @@ -import { get, set } from '@ember/object'; +import { set } from '@ember/object'; import { inject as service } from '@ember/service'; import { Promise } from 'rsvp'; +// @ts-expect-error TODO: not yet typed import TabRoute from 'ember-inspector/routes/tab'; -import PromiseAssembler from 'ember-inspector/libs/promise-assembler'; + +import PromiseAssembler from '../libs/promise-assembler'; +import type PortService from '../services/port'; export default class PromiseTreeRoute extends TabRoute { - @service port; + @service declare port: PortService; + + assembler: PromiseAssembler; + + constructor() { + super(...arguments); - assembler = PromiseAssembler.create({ - port: this.port, - }); + this.assembler = PromiseAssembler.create({ + port: this.port, + }); + } model() { // block rendering until first batch arrives // Helps prevent flashing of "please refresh the page" return new Promise((resolve) => { this.assembler.one('firstMessageReceived', () => { - resolve(get(this, 'assembler.topSort')); + resolve(this.assembler.topSort); }); this.assembler.start(); }); diff --git a/app/services/port.ts b/app/services/port.ts index c521b839fd..d01d8b6e69 100644 --- a/app/services/port.ts +++ b/app/services/port.ts @@ -108,17 +108,17 @@ export default class PortService extends Service { // Manually implement Evented functionality, so we can move away from the mixin @action - on(eventName: string, target: unknown, method: AnyFn) { + on(eventName: string, target: unknown = this, method: AnyFn) { addListener(this, eventName, target, method); } @action - one(eventName: string, target: unknown, method: AnyFn) { + one(eventName: string, target: unknown = this, method: AnyFn) { addListener(this, eventName, target, method, true); } @action - off(eventName: string, target: unknown, method: AnyFn) { + off(eventName: string, target: unknown = this, method: AnyFn) { try { removeListener(this, eventName, target, method); } catch (e) { diff --git a/app/services/storage.js b/app/services/storage.ts similarity index 83% rename from app/services/storage.js rename to app/services/storage.ts index a7fe5b940d..63c5872ac9 100644 --- a/app/services/storage.js +++ b/app/services/storage.ts @@ -1,5 +1,7 @@ import Service, { inject as service } from '@ember/service'; import { LOCAL_STORAGE_SUPPORTED } from './storage/local'; +import type LocalStorageService from './storage/local'; +import type MemoryStorageService from './storage/memory'; /** * Service that wraps either the LocalStorageService or @@ -12,16 +14,14 @@ import { LOCAL_STORAGE_SUPPORTED } from './storage/local'; */ export default class StorageService extends Service { @service(LOCAL_STORAGE_SUPPORTED ? 'storage/local' : 'storage/memory') - backend; + declare backend: LocalStorageService | MemoryStorageService; /** * Reads a stored object for a give key, if any. * - * @method getItem - * @param {String} key * @return {Option} The value, if found */ - getItem(key) { + getItem(key: string) { let serialized = this.backend.getItem(key); if (serialized === null) { @@ -34,12 +34,8 @@ export default class StorageService extends Service { /** * Store a string for a given key. - * - * @method setItem - * @param {String} key - * @param {String} value */ - setItem(key, value) { + setItem(key: string, value: string) { if (value === undefined) { this.removeItem(key); } else { @@ -50,18 +46,14 @@ export default class StorageService extends Service { /** * Deletes the stored string for a given key. - * - * @method removeItem - * @param {String} key */ - removeItem(key) { + removeItem(key: string) { this.backend.removeItem(key); } /** * Returns the list of stored keys. * - * @method keys * @return {Array} The array of keys */ keys() { diff --git a/app/services/storage/local.js b/app/services/storage/local.ts similarity index 75% rename from app/services/storage/local.js rename to app/services/storage/local.ts index 9d4d89ccee..b98a3f9b59 100644 --- a/app/services/storage/local.js +++ b/app/services/storage/local.ts @@ -6,48 +6,35 @@ export let LOCAL_STORAGE_SUPPORTED = false; * Service that wraps local storage. Only store strings. This * is not intended to be used directly, use StorageServeice * instead. - * - * @class LocalStorageService - * @extends Service */ export default class LocalStorageService extends Service { /** * Reads a stored string for a give key, if any. * - * @method getItem - * @param {String} key * @return {Option} The value, if found */ - getItem(key) { + getItem(key: string) { return localStorage.getItem(key); } /** * Store a string for a given key. - * - * @method setItem - * @param {String} key - * @param {String} value */ - setItem(key, value) { + setItem(key: string, value: string) { localStorage.setItem(key, value); } /** * Deletes the stored string for a given key. - * - * @method removeItem - * @param {String} key */ - removeItem(key) { + removeItem(key: string) { localStorage.removeItem(key); } /** * Returns the list of stored keys. * - * @method keys - * @return {Array} The array of keys + * @return The array of keys */ keys() { let keys = []; diff --git a/app/services/storage/memory.js b/app/services/storage/memory.ts similarity index 67% rename from app/services/storage/memory.js rename to app/services/storage/memory.ts index a912fb3874..a49deec626 100644 --- a/app/services/storage/memory.js +++ b/app/services/storage/memory.ts @@ -4,17 +4,12 @@ import Service from '@ember/service'; * Service that wraps an in-memory object with the same APIs as * the LocalStorageService, usually as a fallback. Only store * strings. This is not intended to be used directly, use - * StorageServeice instead. - * - * @class MemoryStorageService - * @extends Service + * StorageService instead. */ export default class MemoryStorageService extends Service { /** * Where data is stored. * - * @property store - * @type {Object} * @private */ store = Object.create(null); @@ -22,11 +17,9 @@ export default class MemoryStorageService extends Service { /** * Reads a stored string for a give key, if any. * - * @method getItem - * @param {String} key * @return {Option} The value, if found */ - getItem(key) { + getItem(key: string) { if (key in this.store) { return this.store[key]; } else { @@ -36,30 +29,22 @@ export default class MemoryStorageService extends Service { /** * Store a string for a given key. - * - * @method setItem - * @param {String} key - * @param {String} value */ - setItem(key, value) { + setItem(key: string, value: string) { this.store[key] = value; } /** * Deletes the stored string for a given key. - * - * @method removeItem - * @param {String} key */ - removeItem(key) { + removeItem(key: string) { delete this.store[key]; } /** * Returns the list of stored keys. * - * @method keys - * @return {Array} The array of keys + * @return The array of keys */ keys() { return Object.keys(this.store); diff --git a/app/utils/nullish.ts b/app/utils/nullish.ts new file mode 100644 index 0000000000..2b42a1c412 --- /dev/null +++ b/app/utils/nullish.ts @@ -0,0 +1,7 @@ +export const isNullish = ( + argument: T | undefined | null, +): argument is undefined | null => argument === null || argument === undefined; + +export const nonNullish = ( + argument: T | undefined | null, +): argument is NonNullable => !isNullish(argument); diff --git a/tsconfig.json b/tsconfig.json index 406c4eb44a..becf7bb970 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ // layout, which is not resolvable with the Node resolution algorithm, to // work with TypeScript. "baseUrl": ".", + "lib": ["ES2023", "DOM"], "paths": { "ember-debug/deps/*": ["node_modules/*"], "ember-debug/*": ["ember_debug/*"], From df817024ff6736531c26de458777708a87b95a59 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Wed, 15 Jan 2025 10:28:36 -0500 Subject: [PATCH 2/3] Fix TS issues --- app/libs/promise-assembler.ts | 36 ++++++++++++++++++++++++++++------ app/routes/promise-tree.ts | 3 ++- app/services/adapters/basic.ts | 4 ++-- app/services/port.ts | 36 ++++++++++++++++++++++++++++------ package.json | 3 ++- 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/app/libs/promise-assembler.ts b/app/libs/promise-assembler.ts index 106ee10fb5..c301fa180d 100644 --- a/app/libs/promise-assembler.ts +++ b/app/libs/promise-assembler.ts @@ -190,20 +190,44 @@ export default class PromiseAssembler extends EmberObject { // Manually implement Evented functionality, so we can move away from the mixin + on(eventName: string, method: AnyFn): void; + on(eventName: string, target: unknown, method: AnyFn): void; + @action - on(eventName: string, target: unknown = this, method: AnyFn) { - addListener(this, eventName, target, method); + on(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn): void { + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + addListener(this, eventName, this, targetOrMethod as AnyFn); + } else { + addListener(this, eventName, targetOrMethod, method!); + } } + one(eventName: string, method: AnyFn): void; + one(eventName: string, target: unknown, method: AnyFn): void; + @action - one(eventName: string, target: unknown = this, method: AnyFn) { - addListener(this, eventName, target, method, true); + one(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn) { + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + addListener(this, eventName, this, targetOrMethod as AnyFn, true); + } else { + addListener(this, eventName, targetOrMethod, method!, true); + } } + off(eventName: string, method: AnyFn): void; + off(eventName: string, target: unknown, method: AnyFn): void; + @action - off(eventName: string, target: unknown = this, method: AnyFn) { + off(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn) { try { - removeListener(this, eventName, target, method); + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + removeListener(this, eventName, this, targetOrMethod as AnyFn); + } else { + removeListener(this, eventName, targetOrMethod, method!); + } } catch (e) { console.error(e); } diff --git a/app/routes/promise-tree.ts b/app/routes/promise-tree.ts index b497e1573b..fd9c0ea19d 100644 --- a/app/routes/promise-tree.ts +++ b/app/routes/promise-tree.ts @@ -51,7 +51,8 @@ export default class PromiseTreeRoute extends TabRoute { ); } - setInstrumentWithStack(message) { + setInstrumentWithStack(message: { instrumentWithStack: boolean }) { + // @ts-expect-error TODO: fix this type later set(this, 'controller.instrumentWithStack', message.instrumentWithStack); } } diff --git a/app/services/adapters/basic.ts b/app/services/adapters/basic.ts index ff12ae3482..7d05937c3d 100644 --- a/app/services/adapters/basic.ts +++ b/app/services/adapters/basic.ts @@ -19,7 +19,7 @@ import config from 'ember-inspector/config/environment'; import type PortService from '../port'; import type { Message } from '../port'; -export default abstract class Basic extends Service { +export default class Basic extends Service { @service declare port: PortService; _messageCallbacks: Array; @@ -100,7 +100,7 @@ export default abstract class Basic extends Service { }); } - abstract reloadTab(): void; + reloadTab?(): void; // Called when the "Reload" is clicked by the user willReload() {} openResource(_file: string, _line: number) {} diff --git a/app/services/port.ts b/app/services/port.ts index d01d8b6e69..9d5f047859 100644 --- a/app/services/port.ts +++ b/app/services/port.ts @@ -107,20 +107,44 @@ export default class PortService extends Service { // Manually implement Evented functionality, so we can move away from the mixin + on(eventName: string, method: AnyFn): void; + on(eventName: string, target: unknown, method: AnyFn): void; + @action - on(eventName: string, target: unknown = this, method: AnyFn) { - addListener(this, eventName, target, method); + on(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn): void { + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + addListener(this, eventName, this, targetOrMethod as AnyFn); + } else { + addListener(this, eventName, targetOrMethod, method!); + } } + one(eventName: string, method: AnyFn): void; + one(eventName: string, target: unknown, method: AnyFn): void; + @action - one(eventName: string, target: unknown = this, method: AnyFn) { - addListener(this, eventName, target, method, true); + one(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn) { + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + addListener(this, eventName, this, targetOrMethod as AnyFn, true); + } else { + addListener(this, eventName, targetOrMethod, method!, true); + } } + off(eventName: string, method: AnyFn): void; + off(eventName: string, target: unknown, method: AnyFn): void; + @action - off(eventName: string, target: unknown = this, method: AnyFn) { + off(eventName: string, targetOrMethod: unknown | AnyFn, method?: AnyFn) { try { - removeListener(this, eventName, target, method); + if (typeof targetOrMethod === 'function') { + // If we did not pass a target, default to `this` + removeListener(this, eventName, this, targetOrMethod as AnyFn); + } else { + removeListener(this, eventName, targetOrMethod, method!); + } } catch (e) { console.error(e); } diff --git a/package.json b/package.json index d93b0d141a..48fc2244c7 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,10 @@ "lint:hbs": "ember-template-lint .", "lint:hbs:fix": "ember-template-lint . --fix", "lint:js": "eslint . --cache", + "lint:js:fix": "eslint . --fix", + "lint:types": "tsc --noEmit", "lock-version": "pnpm build:production && pnpm compress:panes", "serve:bookmarklet": "ember serve --port 9191", - "lint:js:fix": "eslint . --fix", "start": "ember serve", "test": "concurrently \"pnpm:lint\" \"pnpm:test:*\" --names \"lint,test:\"", "test:ember": "COVERAGE=true ember test", From 88f6626b391de1d63a30b464de4dea07d904cc53 Mon Sep 17 00:00:00 2001 From: Robbie Wagner Date: Wed, 15 Jan 2025 11:26:32 -0500 Subject: [PATCH 3/3] Fix incorrect boolean --- app/libs/promise-assembler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/libs/promise-assembler.ts b/app/libs/promise-assembler.ts index c301fa180d..0f7e56cfa2 100644 --- a/app/libs/promise-assembler.ts +++ b/app/libs/promise-assembler.ts @@ -109,7 +109,7 @@ export default class PromiseAssembler extends EmberObject { let guid = promise.guid; let meta = topSortMeta[guid] ?? {}; let isNew = !meta; - let hadParent = false; + let hadParent: boolean | undefined = false; let hasParent = !!promise.parent; let topSort = this.topSort; let parentChanged = isNew; @@ -117,7 +117,7 @@ export default class PromiseAssembler extends EmberObject { if (isNew) { meta = topSortMeta[guid] = {}; } else { - hadParent = Boolean(meta.hasParent); + hadParent = meta.hasParent; } if (!isNew && hasParent !== hadParent) { // todo: implement recursion to reposition children