diff --git a/packages/@ember/debug/ember-inspector-support/libs/render-tree.ts b/packages/@ember/debug/ember-inspector-support/libs/render-tree.ts index 607b64ea059..09197f2900a 100644 --- a/packages/@ember/debug/ember-inspector-support/libs/render-tree.ts +++ b/packages/@ember/debug/ember-inspector-support/libs/render-tree.ts @@ -1,42 +1,42 @@ import captureRenderTree from './capture-render-tree'; import { guidFor } from '@ember/debug/ember-inspector-support/utils/ember/object/internals'; -import { - EmberLoader, - emberSafeRequire, -} from '@ember/debug/ember-inspector-support/utils/ember/loader'; import { inspect } from '@ember/debug/ember-inspector-support/utils/type-check'; -import { isInVersionSpecifier } from '@ember/debug/ember-inspector-support/utils/version'; -import { VERSION } from '@ember/debug/ember-inspector-support/utils/ember'; +import { CustomModifierManager } from '@glimmer/manager'; +import * as GlimmerRuntime from '@glimmer/runtime'; +import type { CapturedRenderNode } from '@glimmer/interfaces'; + +declare module '@glimmer/interfaces' { + interface CapturedRenderNode { + meta: { + parentElement: HTMLBaseElement; + }; + } +} class InElementSupportProvider { - constructor(owner) { + nodeMap: Map; + remoteRoots: CapturedRenderNode[]; + Wormhole: any; + debugRenderTree: any; + NewElementBuilder: any; + debugRenderTreeFunctions: { exit: any; enter: any } | undefined; + NewElementBuilderFunctions: any; + constructor(owner: any) { this.nodeMap = new Map(); this.remoteRoots = []; - this.runtime = this.require('@glimmer/runtime'); - this.reference = this.require('@glimmer/reference'); try { + // @ts-expect-error expected error this.Wormhole = requireModule('ember-wormhole/components/ember-wormhole'); - } catch (e) { + } catch { // nope } - try { - requireModule('@glimmer/manager').CustomModifierManager.prototype.getDebugInstance = (args) => - args.modifier || args.delegate; - } catch (e) { - // nope - } - - this.DESTROY = emberSafeRequire('@glimmer/util')?.DESTROY; - this.registerDestructor = - emberSafeRequire('@glimmer/destroyable')?.registerDestructor || - emberSafeRequire('@ember/destroyable')?.registerDestructor || - emberSafeRequire('@ember/runtime')?.registerDestructor; + CustomModifierManager.prototype.getDebugInstance = (args) => args.modifier || args.delegate; this.debugRenderTree = owner.lookup('renderer:-dom')?.debugRenderTree || owner.lookup('service:-glimmer-environment')._debugRenderTree; - this.NewElementBuilder = this.runtime.NewElementBuilder; + this.NewElementBuilder = GlimmerRuntime.NewElementBuilder; this.patch(); } @@ -47,45 +47,14 @@ class InElementSupportProvider { } patch() { - const self = this; - - const NewElementBuilder = this.NewElementBuilder; - const componentStack = []; - - const enableModifierSupport = - isInVersionSpecifier('>3.28.0', VERSION) && !isInVersionSpecifier('>5.9.0', VERSION); - const hasModifierAndInElementSupport = isInVersionSpecifier('>5.9.0', VERSION); - - function createRef(value) { - if (self.reference.createUnboundRef) { - return self.reference.createUnboundRef(value); - } else { - return value; - } - } - - function createArgs(args) { - if (self.reference.createUnboundRef) { - return args; - } else { - return { - value: () => args, - }; - } - } - - const appendChild = this.debugRenderTree.appendChild; - this.debugRenderTree.appendChild = function (node, state) { - if (node.type === 'component' || node.type === 'keyword') { - componentStack.push(node); - } - return appendChild.call(this, node, state); - }; - - let currentElement = null; + const NewElementBuilder = GlimmerRuntime.NewElementBuilder; + const componentStack: any[] = []; + let currentElement: any = null; const captureNode = this.debugRenderTree.captureNode; - this.debugRenderTree.captureNode = function (id, state) { + // this adds meta to the capture + // https://github.com/glimmerjs/glimmer-vm/pull/1575 + this.debugRenderTree.captureNode = function (id: string, state: any) { const node = this.nodeFor(state); const res = captureNode.call(this, id, state); res.meta = node.meta; @@ -93,7 +62,7 @@ class InElementSupportProvider { }; const exit = this.debugRenderTree.exit; - this.debugRenderTree.exit = function (state) { + this.debugRenderTree.exit = function (state: any) { const node = this.nodeFor(this.stack.current); if (node?.type === 'component' || node.type === 'keyword') { componentStack.pop(); @@ -102,7 +71,9 @@ class InElementSupportProvider { }; const enter = this.debugRenderTree.enter; - this.debugRenderTree.enter = function (...args) { + // this is required to have the original parentElement for in-element + // https://github.com/glimmerjs/glimmer-vm/pull/1575 + this.debugRenderTree.enter = function (...args: any) { enter.call(this, ...args); const node = this.nodeFor(args[0]); if (node?.type === 'keyword' && node.name === 'in-element') { @@ -114,155 +85,37 @@ class InElementSupportProvider { }; const didAppendNode = NewElementBuilder.prototype.didAppendNode; - NewElementBuilder.prototype.didAppendNode = function (...args) { - args[0].__emberInspectorParentNode = componentStack.at(-1); + // just some optimization for search later, not really needed + // @ts-expect-error expected error + NewElementBuilder.prototype.didAppendNode = function (...args: any) { + args[0].__emberInspectorParentNode = componentStack.slice(-1)[0]; + // @ts-expect-error expected error return didAppendNode.call(this, ...args); }; - const pushElement = NewElementBuilder.prototype.pushElement; - NewElementBuilder.prototype.pushElement = function (...args) { + const pushElement = NewElementBuilder.prototype['pushElement']; + NewElementBuilder.prototype['pushElement'] = function (...args: any) { + // @ts-ignore pushElement.call(this, ...args); - args[0].__emberInspectorParentNode = componentStack.at(-1); + args[0].__emberInspectorParentNode = componentStack.slice(-1)[0]; }; - const pushModifiers = NewElementBuilder.prototype.pushModifiers; - if (enableModifierSupport && !hasModifierAndInElementSupport) { - NewElementBuilder.prototype.pushModifiers = function (modifiers) { - const debugRenderTree = self.debugRenderTree; - if (debugRenderTree) { - modifiers = modifiers || []; - const modifier = modifiers[0]; - let element = null; - if (modifiers.length) { - element = modifier[1]?.element || modifier.state.element; - } - for (const modifier of modifiers) { - const state = {}; - const modifierState = modifier.state?.instance || modifier.state || modifier[1]; - const instance = modifierState?.instance || modifierState?.delegate; - let name = - modifier.definition?.resolvedName || modifierState?.debugName || instance?.name; - if (!name) { - try { - name = modifier.manager?.getDebugName?.(); - } catch (e) { - // failed - } - name = name || 'unknown-modifier'; - } - const args = { - positional: [], - named: {}, - }; - const positional = - modifierState?.args?.positional?.references || modifierState?.args?.positional || []; - for (const value of positional) { - if (value && value[self.reference.REFERENCE]) { - args.positional.push(value); - } else { - args.positional.push(createRef(value)); - } - } - let named = modifierState?.args?.named; - if (!self.reference.createUnboundRef) { - try { - named = modifierState?.args?.named?.constructor; - } catch (e) { - // - } - try { - named = named || modifierState?.args?.named?.map; - } catch (e) { - // - } - } - for (const [key, value] of Object.entries(named || {})) { - args.named[key] = createRef(value); - } - debugRenderTree?.create(state, { - type: 'modifier', - name, - args: createArgs(args), - instance: instance, - }); - debugRenderTree?.didRender(state, { - parentElement: () => element.parentElement, - firstNode: () => element, - lastNode: () => element, - }); - self.registerDestructor(modifier.state, () => { - debugRenderTree?.willDestroy(state); - }); - } - } - return pushModifiers.call(this, modifiers); - }; - } - + // https://github.com/glimmerjs/glimmer-vm/pull/1575 const pushRemoteElement = NewElementBuilder.prototype.pushRemoteElement; - const popRemoteElement = NewElementBuilder.prototype.popRemoteElement; - if (!hasModifierAndInElementSupport) { - NewElementBuilder.prototype.pushRemoteElement = function (element, guid, insertBefore) { - const ref = createRef(element); - const capturedArgs = { - positional: [ref], - named: {}, - }; - if (insertBefore) { - capturedArgs.named.insertBefore = insertBefore; - } - const debugRenderTree = self.debugRenderTree; - - const r = pushRemoteElement.call(this, element, guid, insertBefore); - const block = this.blockStack.current; - - if (this.DESTROY) { - const destructor = block[this.DESTROY]; - block[this.DESTROY] = function () { - self.debugRenderTree?.willDestroy(block); - destructor.call(this); - }; - } else { - self.registerDestructor?.(block, () => { - self.debugRenderTree?.willDestroy(block); - }); - } - - debugRenderTree?.create(block, { - type: 'keyword', - name: 'in-element', - args: createArgs(capturedArgs), - }); - return r; - }; + NewElementBuilder.prototype.pushRemoteElement = function (...args: any) { + currentElement = this.element; + // @ts-ignore + return pushRemoteElement.call(this, ...args); + }; - NewElementBuilder.prototype.popRemoteElement = function (...args) { - const block = this.blockStack.current; - popRemoteElement.call(this, ...args); - const parentElement = this.element; - const debugRenderTree = self.debugRenderTree; - debugRenderTree?.didRender(block, { - parentElement: () => parentElement, - firstNode: () => block.firstNode(), - lastNode: () => block.lastNode(), - }); - }; - } else { - NewElementBuilder.prototype.pushRemoteElement = function (...args) { - currentElement = this.element; - return pushRemoteElement.call(this, ...args); - }; - } this.debugRenderTreeFunctions = { - appendChild, + enter, exit, }; this.NewElementBuilderFunctions = { pushElement, pushRemoteElement, - popRemoteElement, didAppendNode, - pushModifiers, }; } @@ -274,13 +127,22 @@ class InElementSupportProvider { Object.assign(this.NewElementBuilder.prototype, this.NewElementBuilderFunctions); this.NewElementBuilderFunctions = null; } - - require(req) { - return requireModule.has(req) ? requireModule(req) : EmberLoader.require(req); - } } export default class RenderTree { + tree!: CapturedRenderNode[]; + owner: any; + retainObject: any; + releaseObject: any; + inspectNode: (node: Node) => void; + renderNodeIdPrefix: string; + nodes!: Record; + serialized!: Record; + ranges!: Record; + parentNodes: any; + previouslyRetainedObjects: any; + retainedObjects: any; + inElementSupport: InElementSupportProvider | undefined; /** * Sets up the initial options. * @@ -289,7 +151,17 @@ export default class RenderTree { * - {owner} owner The Ember app's owner. * - {Function} retainObject Called to retain an object for future inspection. */ - constructor({ owner, retainObject, releaseObject, inspectNode }) { + constructor({ + owner, + retainObject, + releaseObject, + inspectNode, + }: { + owner: any; + retainObject: any; + releaseObject: any; + inspectNode: (node: Node) => void; + }) { this.owner = owner; this.retainObject = retainObject; this.releaseObject = releaseObject; @@ -298,8 +170,8 @@ export default class RenderTree { try { this.inElementSupport = new InElementSupportProvider(owner); } catch (e) { - console.error('failed to setup in element support'); - console.error(e); + // eslint-disable-next-line no-console + console.error('failed to setup in element support', e); // not supported } @@ -350,7 +222,7 @@ export default class RenderTree { * @param {string} id A render node id. * @return {Option} A render node with the given id, if any. */ - find(id) { + find(id: string) { let node = this.nodes[id]; if (node) { @@ -368,7 +240,10 @@ export default class RenderTree { * @param {string} hint The id of the last-matched render node (see comment below). * @return {Option} The deepest enclosing render node, if any. */ - findNearest(node, hint) { + findNearest( + node: Node & { __emberInspectorParentElement: any; __emberInspectorParentNode: any }, + hint?: string + ) { // Use the hint if we are given one. When doing "live" inspecting, the mouse likely // hasn't moved far from its last location. Therefore, the matching render node is // likely to be the same render node, one of its children, or its parent. Knowing this, @@ -380,8 +255,8 @@ export default class RenderTree { node = node.__emberInspectorParentElement; } - let hintNode = this._findUp(this.nodes[hint]); - let hints = [hintNode]; + let hintNode = this._findUp(this.nodes[hint!]); + let hints = [hintNode!]; if (node.__emberInspectorParentNode) { const remoteNode = this.inElementSupport?.nodeMap.get(node); const n = remoteNode && this.nodes[remoteNode]; @@ -409,7 +284,7 @@ export default class RenderTree { * @param {*} id A render node id. * @return {Option} The bounding rect, if the render node is found and has valid `bounds`. */ - getBoundingClientRect(id) { + getBoundingClientRect(id: string) { let node = this.nodes[id]; if (!node || !node.bounds) { @@ -424,8 +299,8 @@ export default class RenderTree { let { bounds } = node; let { firstNode } = bounds; - if (isSingleNode(bounds) && firstNode.getBoundingClientRect) { - rect = firstNode.getBoundingClientRect(); + if (isSingleNode(bounds) && (firstNode as unknown as HTMLElement).getBoundingClientRect) { + rect = (firstNode as unknown as HTMLElement).getBoundingClientRect(); } else { rect = this.getRange(id)?.getBoundingClientRect(); } @@ -444,7 +319,7 @@ export default class RenderTree { * @param {string} id A render node id. * @return {Option} The DOM range, if the render node is found and has valid `bounds`. */ - getRange(id) { + getRange(id: string) { let range = this.ranges[id]; if (range === undefined) { @@ -452,8 +327,8 @@ export default class RenderTree { if (node && node.bounds && isAttached(node.bounds)) { range = document.createRange(); - range.setStartBefore(node.bounds.firstNode); - range.setEndAfter(node.bounds.lastNode); + range.setStartBefore(node.bounds.firstNode as unknown as Node); + range.setEndAfter(node.bounds.lastNode as unknown as Node); } else { // If the node has already been detached, we probably have a stale tree range = null; @@ -471,14 +346,14 @@ export default class RenderTree { * @method scrollIntoView * @param {string} id A render node id. */ - scrollIntoView(id) { + scrollIntoView(id: string) { let node = this.nodes[id]; if (!node || node.bounds === null) { return; } - let element = this._findNode(node.bounds, [Node.ELEMENT_NODE]); + let element = this._findNode(node, [Node.ELEMENT_NODE]); if (element) { element.scrollIntoView({ @@ -496,7 +371,7 @@ export default class RenderTree { * @method inspectElement * @param {string} id A render node id. */ - inspectElement(id) { + inspectElement(id: string) { let node = this.nodes[id]; if (!node || node.bounds === null) { @@ -504,7 +379,7 @@ export default class RenderTree { } // We cannot inspect text nodes - let target = this._findNode(node.bounds, [Node.ELEMENT_NODE, Node.COMMENT_NODE]); + let target = this._findNode(node, [Node.ELEMENT_NODE, Node.COMMENT_NODE]); this.inspectNode(target); } @@ -512,12 +387,10 @@ export default class RenderTree { teardown() { this._reset(); this._releaseStaleObjects(); - this.inElementSupport?.teardown(); } _reset() { this.tree = []; - this.inElementSupport?.reset(); this.nodes = Object.create(null); this.parentNodes = Object.create(null); this.serialized = Object.create(null); @@ -526,7 +399,7 @@ export default class RenderTree { this.retainedObjects = new Map(); } - _createSimpleInstance(name, args) { + _createSimpleInstance(name: string, args: any) { const obj = Object.create(null); obj.args = args; obj.constructor = { @@ -536,8 +409,8 @@ export default class RenderTree { return obj; } - _insertHtmlElementNode(node, parentNode) { - const element = node.bounds.firstNode; + _insertHtmlElementNode(node: CapturedRenderNode, parentNode?: CapturedRenderNode | null): any { + const element = node.bounds!.firstNode as unknown as HTMLElement; const htmlNode = { id: node.id + 'html-element', type: 'html-element', @@ -554,22 +427,22 @@ export default class RenderTree { positional: [], }, children: [], - }; - const idx = parentNode.children.indexOf(node); - parentNode.children.splice(idx, 0, htmlNode); + } as unknown as CapturedRenderNode; + const idx = parentNode!.children.indexOf(node); + parentNode!.children.splice(idx, 0, htmlNode); return this._serializeRenderNode(htmlNode, parentNode); } - _serializeRenderNodes(nodes, parentNode = null) { + _serializeRenderNodes(nodes: CapturedRenderNode[], parentNode: CapturedRenderNode | null = null) { const mapped = []; // nodes can be mutated during serialize, which is why we use indexing instead of .map for (let i = 0; i < nodes.length; i++) { - mapped.push(this._serializeRenderNode(nodes[i], parentNode)); + mapped.push(this._serializeRenderNode(nodes[i]!, parentNode)); } return mapped; } - _serializeRenderNode(node, parentNode = null) { + _serializeRenderNode(node: CapturedRenderNode, parentNode: CapturedRenderNode | null = null) { if (!node.id.startsWith(this.renderNodeIdPrefix)) { node.id = `${this.renderNodeIdPrefix}-${node.id}`; } @@ -585,8 +458,6 @@ export default class RenderTree { name: 'InElement', }, }; - this.inElementSupport?.nodeMap.set(node, node.id); - this.inElementSupport?.remoteRoots.push(node); } if ( @@ -597,11 +468,12 @@ export default class RenderTree { const bounds = node.bounds; Object.defineProperty(node, 'bounds', { get() { - if (node.instance._destination) { + const instance = node.instance as any; + if ((node.instance as any)._destination) { return { - firstNode: node.instance._destination, - lastNode: node.instance._destination, - parentElement: node.instance._destination.parentElement, + firstNode: instance._destination, + lastNode: instance._destination, + parentElement: instance._destination.parentElement, }; } return bounds; @@ -613,32 +485,33 @@ export default class RenderTree { this.parentNodes[node.id] = parentNode; } - if (node.type === 'html-element') { + if ((node.type as string) === 'html-element') { // show set attributes in inspector - Array.from(node.instance.attributes).forEach((attr) => { + const instance = node.instance as HTMLElement; + Array.from(instance.attributes).forEach((attr) => { node.args.named[attr.nodeName] = attr.nodeValue; }); // move modifiers and components into the element children - parentNode.children.forEach((child) => { + parentNode!.children.forEach((child) => { if ( - child.bounds.parentElement === node.instance || - child.meta?.parentElement === node.instance || - (child.type === 'modifier' && child.bounds.firstNode === node.instance) + (child as any).bounds.parentElement === instance || + (child as any).meta?.parentElement === instance || + (child.type === 'modifier' && (child as any).bounds.firstNode === instance) ) { node.children.push(child); } }); node.children.forEach((child) => { - const idx = parentNode.children.indexOf(child); + const idx = parentNode!.children.indexOf(child); if (idx >= 0) { - parentNode.children.splice(idx, 1); + parentNode!.children.splice(idx, 1); } }); } if (node.type === 'component' && !node.instance) { if (node.name === '(unknown template-only component)' && node.template?.endsWith('.hbs')) { - node.name = node.template.split(/\\|\//).slice(-1)[0].slice(0, -'.hbs'.length); + node.name = node.template.split(/\\|\//).slice(-1)[0]!.slice(0, -'.hbs'.length); } node.instance = this._createSimpleInstance('TemplateOnlyComponent', node.args.named); } @@ -649,8 +522,8 @@ export default class RenderTree { .replace(/^-/, '') .replace('-modifier', ''); node.instance = node.instance || this._createSimpleInstance(node.name, node.args); - node.instance.toString = () => node.name; - if (parentNode.instance !== node.bounds.firstNode) { + (node.instance as any).toString = () => node.name; + if (parentNode!.instance !== node.bounds!.firstNode) { return this._insertHtmlElementNode(node, parentNode); } } @@ -668,14 +541,14 @@ export default class RenderTree { return serialized; } - _serializeArgs({ named, positional }) { + _serializeArgs({ named, positional }: any) { return { named: this._serializeDict(named), positional: this._serializeArray(positional), }; } - _serializeBounds(bounds) { + _serializeBounds(bounds: any) { if (bounds === null) { return null; } else if (isSingleNode(bounds)) { @@ -685,7 +558,7 @@ export default class RenderTree { } } - _serializeDict(dict) { + _serializeDict(dict: any) { let result = Object.create(null); if ('__ARGS__' in dict) { @@ -699,11 +572,11 @@ export default class RenderTree { return result; } - _serializeArray(array) { + _serializeArray(array: any[]) { return array.map((item) => this._serializeItem(item)); } - _serializeItem(item) { + _serializeItem(item: any) { switch (typeof item) { case 'string': case 'number': @@ -717,7 +590,7 @@ export default class RenderTree { } } - _serializeObject(object) { + _serializeObject(object: any) { let id = this.previouslyRetainedObjects.get(object); if (id === undefined) { @@ -759,17 +632,21 @@ export default class RenderTree { this.previouslyRetainedObjects = null; } - _getParent(id) { + _getParent(id: string) { return this.parentNodes[id] || null; } - _matchRenderNodes(renderNodes, dom, deep = true) { + _matchRenderNodes( + renderNodes: CapturedRenderNode[], + dom: Node, + deep = true + ): CapturedRenderNode | null { let candidates = [...renderNodes]; while (candidates.length > 0) { - let candidate = candidates.shift(); + let candidate = candidates.shift()!; let range = this.getRange(candidate.id); - const isAllowed = candidate.type !== 'modifier' && candidate.type !== 'html-element'; + const isAllowed = candidate.type !== 'modifier' && (candidate as any).type !== 'html-element'; if (!isAllowed) { candidates.push(...candidate.children); @@ -795,21 +672,21 @@ export default class RenderTree { return null; } - _findNode(bounds, nodeTypes) { - let node = bounds.firstNode; + _findNode(capturedNode: CapturedRenderNode, nodeTypes: number[]): HTMLBaseElement { + let node = capturedNode.bounds!.firstNode; do { if (nodeTypes.indexOf(node.nodeType) > -1) { - return node; + return node as unknown as HTMLBaseElement; } else { - node = node.nextSibling; + node = node.nextSibling!; } - } while (node && node !== bounds.lastNode); + } while (node && node !== capturedNode.bounds!.lastNode); - return node.meta?.parentElement || bounds.parentElement; + return capturedNode.meta?.parentElement || capturedNode.bounds!.parentElement; } - _findUp(node) { + _findUp(node?: CapturedRenderNode) { // Find the first parent render node with a different enclosing DOM element. // Usually, this is just the first parent render node, but there are cases where // multiple render nodes share the same bounds (e.g. outlet -> route template). @@ -833,14 +710,14 @@ export default class RenderTree { } } -function isSingleNode({ firstNode, lastNode }) { +function isSingleNode({ firstNode, lastNode }: any) { return firstNode === lastNode; } -function isAttached({ firstNode, lastNode }) { +function isAttached({ firstNode, lastNode }: any) { return firstNode.isConnected && lastNode.isConnected; } -function isEmptyRect({ x, y, width, height }) { +function isEmptyRect({ x, y, width, height }: any) { return x === 0 && y === 0 && width === 0 && height === 0; } diff --git a/packages/@ember/debug/ember-inspector-support/libs/view-inspection.ts b/packages/@ember/debug/ember-inspector-support/libs/view-inspection.ts index fd066ab8700..abceb6138a7 100644 --- a/packages/@ember/debug/ember-inspector-support/libs/view-inspection.ts +++ b/packages/@ember/debug/ember-inspector-support/libs/view-inspection.ts @@ -1,12 +1,14 @@ import classify from '@ember/debug/ember-inspector-support/utils/classify'; import bound from '@ember/debug/ember-inspector-support/utils/bound-method'; import getObjectName from '../utils/get-object-name'; +import type RenderTree from './render-tree'; +import type ObjectInspector from '../object-inspector'; -function makeHighlight(id) { +function makeHighlight(id: string) { return ``; } -function makeTooltip(id) { +function makeTooltip(id: string) { let prefix = 'ember-inspector-tooltip'; return ` @@ -226,6 +228,23 @@ function makeStylesheet(id) { } export default class ViewInspection { + renderTree: RenderTree; + objectInspector: ObjectInspector; + private didShow: boolean; + private didHide: boolean; + private didStartInspecting: boolean; + private didStopInspecting: boolean; + private id: string; + private currentId: string; + private lastMatchId: string; + private isInspecting: boolean; + private lastTarget: boolean; + private isShowing: boolean; + private isPinned: boolean; + + private highlight: HTMLElement; + private tooltip: HTMLElement; + private stylesheet: HTMLElement; constructor({ renderTree, objectInspector, @@ -233,6 +252,13 @@ export default class ViewInspection { didHide, didStartInspecting, didStopInspecting, + }: { + renderTree: RenderTree; + objectInspector: ObjectInspector; + didShow: boolean; + didHide: boolean; + didStartInspecting: boolean; + didStopInspecting: boolean; }) { this.renderTree = renderTree; this.objectInspector = objectInspector; @@ -641,7 +667,7 @@ export default class ViewInspection { return [['tag', stringified]]; } - _positionTooltip(highlightRect) { + _positionTooltip(highlightRect: DOMRect) { // Positioning the tooltip: the goal is to match the Chrome's Element // inspection tooltip's positioning behavior as closely as possible. @@ -710,20 +736,20 @@ export default class ViewInspection { tooltipStyle.top = `${scrollY + attachmentTop}px`; tooltipStyle.left = `${scrollX + attachmentLeft - leftOffset}px`; - let arrow = this.tooltip.querySelector('.ember-inspector-tooltip-arrow'); + let arrow = this.tooltip.querySelector('.ember-inspector-tooltip-arrow')! as HTMLElement; arrow.style.left = `${Math.max(leftOffset, 0) + arrowLeft}px`; } - _insertHTML(html) { + _insertHTML(html: string) { document.body.insertAdjacentHTML('beforeend', html.trim()); - return document.body.lastChild; + return document.body.lastChild as HTMLElement; } - _insertStylesheet(content) { + _insertStylesheet(content: string) { let style = document.createElement('style'); style.appendChild(document.createTextNode(content)); document.head.appendChild(style); - return style; + return style as HTMLElement; } } diff --git a/packages/@ember/debug/ember-inspector-support/models/profile-manager.ts b/packages/@ember/debug/ember-inspector-support/models/profile-manager.ts index eefbb0ff0fa..944cc4158f3 100644 --- a/packages/@ember/debug/ember-inspector-support/models/profile-manager.ts +++ b/packages/@ember/debug/ember-inspector-support/models/profile-manager.ts @@ -9,7 +9,7 @@ type Bounds = { parent: HTMLElement; }; -function getEdges(first: any, last: any, closest: any[]) { +function getEdges(first: any, last: any, closest: any) { let start = null; let end = null; for (let i = 0; i < closest.length; i++) { @@ -68,11 +68,11 @@ function insertStylesheet() { type Info = { type: string; - endedIndex: any; - now: number; + endedIndex?: any; + now?: number; timestamp: number; payload: Payload; - profileNode: ProfileNode; + profileNode?: ProfileNode; }; type Highlight = { @@ -248,11 +248,11 @@ export default class ProfileManager { this.queue[entry.endedIndex]!.profileNode = this.began( entry.timestamp, entry.payload, - entry.now + entry.now! ); } } else { - this.ended(entry.timestamp, entry.payload, entry.profileNode); + this.ended(entry.timestamp, entry.payload, entry.profileNode!); } } this.queue.length = 0; diff --git a/packages/@ember/debug/ember-inspector-support/object-inspector.ts b/packages/@ember/debug/ember-inspector-support/object-inspector.ts index 0870c7003f3..35ef66546bb 100644 --- a/packages/@ember/debug/ember-inspector-support/object-inspector.ts +++ b/packages/@ember/debug/ember-inspector-support/object-inspector.ts @@ -1,82 +1,23 @@ import DebugPort from './debug-port'; import bound from '@ember/debug/ember-inspector-support/utils/bound-method'; import { - isComputed, - getDescriptorFor, typeOf, inspect, } from '@ember/debug/ember-inspector-support/utils/type-check'; -import { compareVersion } from '@ember/debug/ember-inspector-support/utils/version'; -import { cacheFor, guidFor } from '@ember/object/internals'; +import { cacheFor } from '@ember/object/internals'; import { _backburner, join } from '@ember/runloop'; +import EmberObject from '@ember/object'; +import Service from '@ember/service'; +import CoreObject from '@ember/object/core'; +import { meta as emberMeta } from '@ember/-internals/meta'; import emberNames from './utils/ember-object-names'; import getObjectName from './utils/get-object-name'; - -const GlimmerComponent = (() => { - try { - return EmberLoader.require('@glimmer/component').default; - } catch (e) { - // ignore, return undefined - } -})(); - -let tagValue, tagValidate, track, tagForProperty; - -try { - // Try to load the most recent library - let GlimmerValidator = EmberLoader.require('@glimmer/validator'); - - tagValue = GlimmerValidator.value || GlimmerValidator.valueForTag; - tagValidate = GlimmerValidator.validate || GlimmerValidator.validateTag; - track = GlimmerValidator.track; - - // patch tagFor to add debug info, older versions already have _propertyKey - const tagFor = GlimmerValidator.tagFor; - GlimmerValidator.tagFor = function (...args) { - const tag = tagFor.call(this, ...args); - const [obj, key] = args; - if ((!tag._propertyKey || !tag._object) && typeof obj === 'object' && typeof key === 'string') { - tag._propertyKey = key; - tag._object = obj; - } - return tag; - }; - const trackedData = GlimmerValidator.trackedData; - GlimmerValidator.trackedData = function (...args) { - const r = trackedData.call(this, ...args); - if (r.getter && args.length === 2) { - const [key] = args; - const getter = r.getter; - r.getter = function (self) { - GlimmerValidator.tagFor(self, key); - return getter.call(this, self); - }; - } - return r; - }; -} catch (e) { - try { - // Fallback to the previous implementation - let GlimmerReference = EmberLoader.require('@glimmer/reference'); - - tagValue = GlimmerReference.value; - tagValidate = GlimmerReference.validate; - } catch (e) { - // ignore - } -} - -try { - let metal = EmberLoader.require('@ember/-internals/metal'); - - tagForProperty = metal.tagForProperty; - // If track was not already loaded, use metal's version (the previous version) - track = track || metal.track; -} catch (e) { - // ignore -} - -const HAS_GLIMMER_TRACKING = tagValue && tagValidate && track && tagForProperty; +import { valueForTag as tagValue, validateTag as tagValidate, track, Tag, tagFor } from "@glimmer/validator"; +import { isComputed } from '@ember/-internals/metal'; +import { guidFor } from './utils/ember/object/internals'; +import Mixin from '@ember/object/mixin'; +import ObjectProxy from '@ember/object/proxy'; +import ArrayProxy from '@ember/array/proxy'; const keys = Object.keys; @@ -87,7 +28,7 @@ const keys = Object.keys; * @param {*} computedValue A value that has already been computed with calculateCP * @return {{inspect: (string|*), type: string}|{computed: boolean, inspect: string, type: string}|{inspect: string, type: string}} */ -function inspectValue(object, key, computedValue) { +function inspectValue(object: any, key: string, computedValue?: any): {type: string, inspect: string, isCalculated?: boolean} { let string; const value = computedValue; @@ -120,7 +61,7 @@ function inspectValue(object, key, computedValue) { } } -function isMandatorySetter(descriptor) { +function isMandatorySetter(descriptor: PropertyDescriptor) { if ( descriptor.set && Function.prototype.toString.call(descriptor.set).includes('You attempted to update') @@ -130,104 +71,118 @@ function isMandatorySetter(descriptor) { return false; } -function getTagTrackedTags(tag, ownTag, level = 0) { - const props = []; +function getTagTrackedTags(tag: Tag, ownTag: Tag, level = 0) { + const props: Tag[] = []; // do not include tracked properties from dependencies if (!tag || level > 1) { return props; } - const subtags = tag.subtags || (Array.isArray(tag.subtag) ? tag.subtag : []); + const subtags = (Array.isArray(tag.subtag) ? tag.subtag : []); if (tag.subtag && !Array.isArray(tag.subtag)) { - if (tag.subtag._propertyKey) props.push(tag.subtag); - + // if (tag.subtag._propertyKey) props.push(tag.subtag); + // TODO fetch tag metadata object and property key props.push(...getTagTrackedTags(tag.subtag, ownTag, level + 1)); } if (subtags) { subtags.forEach((t) => { if (t === ownTag) return; - if (t._propertyKey) props.push(t); + // if (t._propertyKey) props.push(t); + // TODO fetch tag metadata object and property key props.push(...getTagTrackedTags(t, ownTag, level + 1)); }); } return props; } -function getTrackedDependencies(object, property, tagInfo) { +// TODO needs https://github.com/glimmerjs/glimmer-vm/pull/1489 +function getTrackedDependencies(object: any, property: string, tagInfo: { tag: Tag, revision: number }) { const tag = tagInfo.tag; const proto = Object.getPrototypeOf(object); if (!proto) return []; const cpDesc = emberMeta(object).peekDescriptors(property); const dependentKeys = []; if (cpDesc) { - dependentKeys.push(...(cpDesc._dependentKeys || []).map((k) => ({ name: k }))); + dependentKeys.push(...(cpDesc._dependentKeys || []).map((k: string) => ({ name: k }))); } - if (HAS_GLIMMER_TRACKING) { - const ownTag = tagForProperty(object, property); - const tags = getTagTrackedTags(tag, ownTag); - const mapping = {}; - let maxRevision = tagValue(tag); - tags.forEach((t) => { - const p = (t._object ? getObjectName(t._object) + '.' : '') + t._propertyKey; - const [objName, prop] = p.split('.'); - mapping[objName] = mapping[objName] || new Set(); - const value = tagValue(t); - if (prop) { - mapping[objName].add([prop, value]); - } - }); - - const hasChange = (tagInfo.revision && maxRevision !== tagInfo.revision) || false; - - const names = new Set(); - - Object.entries(mapping).forEach(([objName, props]) => { - if (names.has(objName)) { - return; - } - names.add(objName); - if (props.size > 1) { - dependentKeys.push({ name: objName }); - props.forEach((p) => { - const changed = hasChange && p[1] > tagInfo.revision; - const obj = { - child: p[0], - }; - if (changed) { - obj.changed = true; - } - dependentKeys.push(obj); - }); - } - if (props.size === 1) { - const p = [...props][0]; + const ownTag = tagFor(object, property); + const tags = getTagTrackedTags(tag, ownTag); + const mapping: Record = {}; + let maxRevision = tagValue(tag); + tags.forEach(() => { + // TODO needs https://github.com/glimmerjs/glimmer-vm/pull/1489 + // const p = (t._object ? getObjectName(t._object) + '.' : '') + t._propertyKey; + // const [objName, prop] = p.split('.'); + // mapping[objName] = mapping[objName] || new Set(); + // const value = tagValue(t); + // if (prop) { + // mapping[objName].add([prop, value]); + // } + }); + const hasChange = (tagInfo.revision && maxRevision !== tagInfo.revision) || false; + const names = new Set(); + Object.entries(mapping).forEach(([objName, props]) => { + if (names.has(objName)) { + return; + } + names.add(objName); + if (props.length > 1) { + dependentKeys.push({ name: objName }); + props.forEach((p) => { const changed = hasChange && p[1] > tagInfo.revision; const obj = { - name: objName + '.' + p[0], + child: p[0], + changed: false, }; if (changed) { obj.changed = true; } dependentKeys.push(obj); + }); + } + if (props.length === 1) { + const p = [...props][0]!; + const changed = hasChange && p[1] > tagInfo.revision; + const obj = { + name: objName + '.' + p[0], + changed: false, + }; + if (changed) { + obj.changed = true; } - if (props.size === 0) { - dependentKeys.push({ name: objName }); - } - }); - } + dependentKeys.push(obj); + } + if (props.length === 0) { + dependentKeys.push({ name: objName }); + } + }); return [...dependentKeys]; } +type MixinDetails = { id: string; name: string, properties: unknown[], expand?: boolean, isEmberMixin?: boolean }; + export default class ObjectInspector extends DebugPort { get adapter() { return this.namespace?.adapter; } - get port() { + get port(): any { return this.namespace?.port; } - currentObject = null; + currentObject: { + object: any; + mixinDetails: { + properties: { + canTrack: boolean; + name: any; + value: any; + isExpensive: boolean; + overridden: boolean; + }[]; + }[]; + objectId: string; + } | null = null; updateCurrentObject() { Object.values(this.sentObjects).forEach((obj) => { @@ -256,12 +211,12 @@ export default class ObjectInspector extends DebugPort { const desc = Object.getOwnPropertyDescriptor(object, item.name); const isSetter = desc && isMandatorySetter(desc); - if (HAS_GLIMMER_TRACKING && item.canTrack && !isSetter) { + if (item.canTrack && !isSetter) { let tagInfo = tracked[item.name] || { - tag: tagForProperty(object, item.name), + tag: tagFor(object, item.name), revision: 0, }; - if (!tagInfo.tag) return; + if (!tagInfo.tag) return false; changed = !tagValidate(tagInfo.tag, tagInfo.revision); if (changed) { @@ -279,7 +234,7 @@ export default class ObjectInspector extends DebugPort { } if (changed) { - value = inspectValue(object, item.name, value); + value = inspectValue(object, item.name, value) as any; value.isCalculated = true; let dependentKeys = null; if (tracked[item.name]) { @@ -300,6 +255,7 @@ export default class ObjectInspector extends DebugPort { } catch (e) { // dont do anything } + return false; }); }); } @@ -319,26 +275,26 @@ export default class ObjectInspector extends DebugPort { _backburner.off('end', bound(this, this.updateCurrentObject)); } - sentObjects = {}; + sentObjects: Record = {}; - parentObjects = {}; + parentObjects: Record = {}; - objectPropertyValues = {}; + objectPropertyValues: Record> = {}; - trackedTags = {}; + trackedTags: Record> = {}; - _errorsFor = {}; + _errorsFor: Record> = {}; static { this.prototype.portNamespace = 'objectInspector'; this.prototype.messages = { - digDeeper(message) { + digDeeper(this: ObjectInspector, message: { objectId: any; property: any; }) { this.digIntoObject(message.objectId, message.property); }, - releaseObject(message) { + releaseObject(this: ObjectInspector, message: { objectId: any; }) { this.releaseObject(message.objectId); }, - calculate(message) { + calculate(this: ObjectInspector, message: { objectId: string | number; property: string; mixinIndex: number; isCalculated: boolean; }) { let value; value = this.valueForObjectProperty(message.objectId, message.property, message.mixinIndex); if (value) { @@ -350,28 +306,28 @@ export default class ObjectInspector extends DebugPort { errors: errorsToSend(this._errorsFor[message.objectId]), }); }, - saveProperty(message) { + saveProperty(this: ObjectInspector, message: { value: any; dataType: string; objectId: string; property: string; }) { let value = message.value; if (message.dataType && message.dataType === 'date') { value = new Date(value); } this.saveProperty(message.objectId, message.property, value); }, - sendToConsole(message) { + sendToConsole(this: ObjectInspector, message: { objectId: any; property: string; }) { this.sendToConsole(message.objectId, message.property); }, - gotoSource(message) { + gotoSource(this: ObjectInspector, message: { objectId: any; property: string; }) { this.gotoSource(message.objectId, message.property); }, - sendControllerToConsole(message) { + sendControllerToConsole(this: ObjectInspector, message: { name: string; }) { const container = this.namespace?.owner; this.sendValueToConsole(container.lookup(`controller:${message.name}`)); }, - sendRouteHandlerToConsole(message) { + sendRouteHandlerToConsole(this: ObjectInspector, message: { name: string; }) { const container = this.namespace?.owner; this.sendValueToConsole(container.lookup(`route:${message.name}`)); }, - sendContainerToConsole() { + sendContainerToConsole(this: ObjectInspector) { const container = this.namespace?.owner; this.sendValueToConsole(container); }, @@ -380,36 +336,31 @@ export default class ObjectInspector extends DebugPort { * @param message The message sent * @param {string} messsage.name The name of the route to lookup */ - inspectRoute(message) { + inspectRoute(this: ObjectInspector, message: { name: string; }) { const container = this.namespace?.owner; const router = container.lookup('router:main'); const routerLib = router._routerMicrolib || router.router; // 3.9.0 removed intimate APIs from router // https://github.com/emberjs/ember.js/pull/17843 // https://deprecations.emberjs.com/v3.x/#toc_remove-handler-infos - if (compareVersion(VERSION, '3.9.0') !== -1) { - // Ember >= 3.9.0 - this.sendObject(routerLib.getRoute(message.name)); - } else { - // Ember < 3.9.0 - this.sendObject(routerLib.getHandler(message.name)); - } + // Ember >= 3.9.0 + this.sendObject(routerLib.getRoute(message.name)); }, - inspectController(message) { + inspectController(this: ObjectInspector, message: { name: string; }) { const container = this.namespace?.owner; this.sendObject(container.lookup(`controller:${message.name}`)); }, - inspectById(message) { - const obj = this.sentObjects[message.objectId]; + inspectById(this: ObjectInspector, message: { objectId: string; }) { + const obj = this.sentObjects[message.objectId]!; if (obj) { this.sendObject(obj); } }, - inspectByContainerLookup(message) { + inspectByContainerLookup(this: ObjectInspector, message: { name: string; }) { const container = this.namespace?.owner; this.sendObject(container.lookup(message.name)); }, - traceErrors(message) { + traceErrors(this: ObjectInspector, message: { objectId: string; }) { let errors = this._errorsFor[message.objectId]; toArray(errors).forEach((error) => { let stack = error.error; @@ -424,7 +375,7 @@ export default class ObjectInspector extends DebugPort { }; } - canSend(val) { + canSend(val: any) { return ( val && (val instanceof EmberObject || @@ -434,7 +385,7 @@ export default class ObjectInspector extends DebugPort { ); } - saveProperty(objectId, prop, val) { + saveProperty(objectId: string, prop: string, val: any) { let object = this.sentObjects[objectId]; join(() => { if (object.set) { @@ -445,7 +396,7 @@ export default class ObjectInspector extends DebugPort { }); } - gotoSource(objectId, prop) { + gotoSource(objectId: string, prop: string) { let object = this.sentObjects[objectId]; let value; @@ -465,7 +416,7 @@ export default class ObjectInspector extends DebugPort { } } - sendToConsole(objectId, prop) { + sendToConsole(objectId: string, prop: string) { let object = this.sentObjects[objectId]; let value; @@ -478,8 +429,8 @@ export default class ObjectInspector extends DebugPort { this.sendValueToConsole(value); } - sendValueToConsole(value) { - window.$E = value; + sendValueToConsole(value: any) { + (window as any).$E = value; if (value instanceof Error) { value = value.stack; } @@ -490,7 +441,7 @@ export default class ObjectInspector extends DebugPort { this.adapter.log('Ember Inspector ($E): ', ...args); } - digIntoObject(objectId, property) { + digIntoObject(objectId: string, property: string) { let parentObject = this.sentObjects[objectId]; let object = calculateCP(parentObject, { name: property }, {}); @@ -509,7 +460,7 @@ export default class ObjectInspector extends DebugPort { } } - sendObject(object) { + sendObject(object: any) { if (!this.canSend(object)) { throw new Error(`Can't inspect ${object}. Only Ember objects and arrays are supported.`); } @@ -522,8 +473,8 @@ export default class ObjectInspector extends DebugPort { }); } - retainObject(object) { - let meta = emberMeta(object); + retainObject(object: any) { + let meta = emberMeta(object) as any; let guid = guidFor(object); meta._debugReferences = meta._debugReferences || 0; @@ -534,12 +485,12 @@ export default class ObjectInspector extends DebugPort { return guid; } - releaseObject(objectId) { + releaseObject(objectId: string) { let object = this.sentObjects[objectId]; if (!object) { return; } - let meta = emberMeta(object); + let meta = emberMeta(object) as any; let guid = guidFor(object); meta._debugReferences--; @@ -549,7 +500,7 @@ export default class ObjectInspector extends DebugPort { } } - dropObject(objectId) { + dropObject(objectId: string) { if (this.parentObjects[objectId]) { this.currentObject = this.parentObjects[objectId]; } @@ -606,7 +557,7 @@ export default class ObjectInspector extends DebugPort { * likely be embedded _on_ the class definitions, but this was designed to be * backwards compatible. */ - mixinDetailsForObject(object) { + mixinDetailsForObject(object: any): MixinDetails[] { const mixins = []; const own = ownMixins(object); @@ -654,19 +605,19 @@ export default class ObjectInspector extends DebugPort { return mixins; } - mixinsForObject(object) { - if (object instanceof ObjectProxy && object.content && !object._showProxyDetails) { + mixinsForObject(object: any) { + if (object instanceof ObjectProxy && object.content && !(object as any)._showProxyDetails) { object = object.content; } - if (object instanceof ArrayProxy && object.content && !object._showProxyDetails) { + if (object instanceof ArrayProxy && object.content && !(object as any)._showProxyDetails) { object = object.slice(0, 101); } let mixinDetails = this.mixinDetailsForObject(object); - mixinDetails[0].name = 'Own Properties'; - mixinDetails[0].expand = true; + mixinDetails[0]!.name = 'Own Properties'; + mixinDetails[0]!.expand = true; if (mixinDetails[1] && !mixinDetails[1].isEmberMixin) { mixinDetails[1].expand = true; @@ -699,7 +650,7 @@ export default class ObjectInspector extends DebugPort { return { objectId, mixins: mixinDetails, errors }; } - valueForObjectProperty(objectId, property, mixinIndex) { + valueForObjectProperty(objectId: string, property: string, mixinIndex: number) { let object = this.sentObjects[objectId], value; @@ -721,11 +672,11 @@ export default class ObjectInspector extends DebugPort { inspectValue = inspectValue; } -function ownMixins(object) { +function ownMixins(object: any) { // TODO: We need to expose an API for getting _just_ the own mixins directly let meta = emberMeta(object); let parentMeta = meta.parent; - let mixins = new Set(); + let mixins = new Set(); // Filter out anonymous mixins that are directly in a `class.extend` let baseMixins = @@ -733,7 +684,7 @@ function ownMixins(object) { object.constructor.PrototypeMixin && object.constructor.PrototypeMixin.mixins; - meta.forEachMixins((m) => { + meta.forEachMixins((m: Mixin) => { // Find mixins that: // - Are not in the parent classes // - Are not primitive (has mixins, doesn't have properties) @@ -751,7 +702,7 @@ function ownMixins(object) { return mixins; } -function ownProperties(object, ownMixins) { +function ownProperties(object: any, ownMixins: Set) { let meta = emberMeta(object); if (Array.isArray(object)) { @@ -759,7 +710,7 @@ function ownProperties(object, ownMixins) { object = object.slice(0, 101); } - let props = Object.getOwnPropertyDescriptors(object); + let props = Object.getOwnPropertyDescriptors(object) as any; delete props.constructor; // meta has the correct descriptors for CPs @@ -777,7 +728,7 @@ function ownProperties(object, ownMixins) { if (m.mixins) { m.mixins.forEach((mix) => { Object.keys(mix.properties || {}).forEach((k) => { - const pDesc = Object.getOwnPropertyDescriptor(mix.properties, k); + const pDesc = Object.getOwnPropertyDescriptor(mix.properties, k) as PropertyDescriptor & { _getter: () => any }; if (pDesc && props[k] && pDesc.get && pDesc.get === props[k].get) { delete props[k]; } @@ -803,8 +754,8 @@ function ownProperties(object, ownMixins) { return addProperties([], props); } -function propertiesForMixin(mixin) { - let properties = []; +function propertiesForMixin(mixin: Mixin) { + let properties: unknown[] = []; if (mixin.mixins) { mixin.mixins.forEach((mixin) => { @@ -817,7 +768,7 @@ function propertiesForMixin(mixin) { return properties; } -function addProperties(properties, hash) { +function addProperties(properties: unknown[], hash: any) { for (let prop in hash) { if (!hash.hasOwnProperty(prop)) { continue; @@ -833,13 +784,26 @@ function addProperties(properties, hash) { } // when mandatory setter is removed, an `undefined` value may be set - const desc = getDescriptorFor(hash, prop) || Object.getOwnPropertyDescriptor(hash, prop); + const desc = Object.getOwnPropertyDescriptor(hash, prop) as any; if (!desc) continue; if (hash[prop] === undefined && desc.value === undefined && !desc.get && !desc._getter) { continue; } - let options = { isMandatorySetter: isMandatorySetter(desc) }; + let options = { + isMandatorySetter: isMandatorySetter(desc), + isService: false, + isComputed: false, + code: undefined as string | undefined, + dependentKeys: undefined, + isCalculated: undefined as boolean | undefined, + readOnly: undefined as boolean | undefined, + auto: undefined as boolean | undefined, + canTrack: undefined as boolean | undefined, + isGetter: undefined as boolean | undefined, + isTracked: undefined as boolean | undefined, + isProperty: undefined as boolean | undefined, + }; if (typeof hash[prop] === 'object' && hash[prop] !== null) { options.isService = !('type' in hash[prop]) && hash[prop].type === 'service'; @@ -861,7 +825,7 @@ function addProperties(properties, hash) { if (isComputed(hash, prop)) { options.isComputed = true; - options.dependentKeys = (desc._dependentKeys || []).map((key) => key.toString()); + options.dependentKeys = (desc._dependentKeys || []).map((key: string) => key.toString()); if (typeof desc.get === 'function') { options.code = Function.prototype.toString.call(desc.get); @@ -898,7 +862,7 @@ function addProperties(properties, hash) { return properties; } -function isInternalProperty(property) { +function isInternalProperty(property: string) { if ( [ '_state', @@ -925,7 +889,7 @@ function isInternalProperty(property) { return isInternalProp; } -function replaceProperty(properties, name, value, options) { +function replaceProperty(properties: any, name: string, value: any, options: any) { let found; let i, l; @@ -940,7 +904,9 @@ function replaceProperty(properties, name, value, options) { properties.splice(i, 1); } - let prop = { name, value }; + let prop = { name, value, + isMandatorySetter: undefined + }; prop.isMandatorySetter = options.isMandatorySetter; prop.readOnly = options.readOnly; prop.auto = options.auto; @@ -1230,6 +1196,6 @@ function calculateCP(object, item, errorsForObject) { function CalculateCPError() {} -function errorsToSend(errors) { +function errorsToSend(errors:) { return toArray(errors).map((error) => ({ property: error.property })); } diff --git a/packages/@ember/debug/ember-inspector-support/render-debug.ts b/packages/@ember/debug/ember-inspector-support/render-debug.ts index ae659b0c95b..f14eb0291c5 100644 --- a/packages/@ember/debug/ember-inspector-support/render-debug.ts +++ b/packages/@ember/debug/ember-inspector-support/render-debug.ts @@ -50,23 +50,26 @@ export default class RenderDebug extends DebugPort { static { this.prototype.portNamespace = 'render'; this.prototype.messages = { - clear() { + clear(this: RenderDebug) { this.profileManager.clearProfiles(); this.sendMessage('profilesUpdated', { profiles: [] }); }, - releaseProfiles() { + releaseProfiles(this: RenderDebug) { this.profileManager.offProfilesAdded(this, this.sendAdded); }, - watchProfiles() { + watchProfiles(this: RenderDebug) { this.sendMessage('profilesAdded', { profiles: this.profileManager.profiles, }); this.profileManager.onProfilesAdded(this, this.sendAdded); }, - updateShouldHighlightRender({ shouldHighlightRender }) { + updateShouldHighlightRender( + this: RenderDebug, + { shouldHighlightRender }: { shouldHighlightRender: boolean } + ) { this.profileManager.shouldHighlightRender = shouldHighlightRender; }, }; @@ -80,7 +83,7 @@ export default class RenderDebug extends DebugPort { */ function _subscribeToRenderEvents() { subscribe('render', { - before(name: string, timestamp, payload) { + before(_name: string, timestamp: number, payload: any) { const info = { type: 'began', timestamp, @@ -90,7 +93,7 @@ function _subscribeToRenderEvents() { return profileManager.addToQueue(info); }, - after(name, timestamp, payload, beganIndex) { + after(_name: string, timestamp, payload: any, beganIndex) { const endedInfo = { type: 'ended', timestamp, @@ -98,7 +101,7 @@ function _subscribeToRenderEvents() { }; const index = profileManager.addToQueue(endedInfo); - profileManager.queue[beganIndex].endedIndex = index; + profileManager.queue[beganIndex]!.endedIndex = index; }, }); } diff --git a/packages/@ember/debug/ember-inspector-support/route-debug.ts b/packages/@ember/debug/ember-inspector-support/route-debug.ts index 8a669c1494b..46bef82b4d2 100644 --- a/packages/@ember/debug/ember-inspector-support/route-debug.ts +++ b/packages/@ember/debug/ember-inspector-support/route-debug.ts @@ -1,7 +1,4 @@ -/* eslint-disable ember/no-private-routing-service */ import DebugPort from './debug-port'; -import { compareVersion } from '@ember/debug/ember-inspector-support/utils/version'; -import { VERSION } from '@ember/debug/ember-inspector-support/utils/ember'; import classify from '@ember/debug/ember-inspector-support/utils/classify'; import dasherize from '@ember/debug/ember-inspector-support/utils/dasherize'; import { _backburner, later } from '@ember/runloop'; @@ -11,6 +8,8 @@ const { hasOwnProperty } = Object.prototype; export default class RouteDebug extends DebugPort { _cachedRouteTree = null; + private __currentURL: any; + private __currentRouter: any; init() { super.init(); this.__currentURL = this.currentURL; @@ -55,16 +54,15 @@ export default class RouteDebug extends DebugPort { static { this.prototype.portNamespace = 'route'; this.prototype.messages = { - getTree() { + getTree(this: RouteDebug) { this.sendTree(); }, - getCurrentRoute() { + getCurrentRoute(this: RouteDebug) { this.sendCurrentRoute(); }, }; } - // eslint-disable-next-line ember/no-observers sendCurrentRoute() { const { currentPath: name, currentURL: url } = this; later(() => { @@ -80,13 +78,13 @@ export default class RouteDebug extends DebugPort { const router = this.router; const routerLib = router._routerMicrolib || router.router; let routeNames = routerLib.recognizer.names; - let routeTree = {}; + let routeTree: Record = {}; for (let routeName in routeNames) { if (!hasOwnProperty.call(routeNames, routeName)) { continue; } let route = routeNames[routeName]; - buildSubTree.call(this, routeTree, route); + this.buildSubTree(routeTree, route); } this._cachedRouteTree = arrayizeChildren({ children: routeTree }); } @@ -98,13 +96,13 @@ export default class RouteDebug extends DebugPort { let error; try { routeTree = this.routeTree; - } catch (e) { + } catch (e: any) { error = e.message; } this.sendMessage('routeTree', { tree: routeTree, error }); } - getClassName(name, type) { + getClassName(name: string, type: string) { let container = this.namespace.owner; let resolver = container.application.__registry__.resolver; let prefix = this.emberCliConfig?.modulePrefix; @@ -152,109 +150,96 @@ export default class RouteDebug extends DebugPort { } return className; } -} -/** - * - * @param {*} routeTree - * @param {*} route - * @this {RouteDebug} - * @return {Void} - */ -function buildSubTree(routeTree, route) { - let handlers = route.handlers; - let owner = this.namespace.owner; - let subTree = routeTree; - let item; - let routeClassName; - let routeHandler; - let controllerName; - let controllerClassName; - let templateName; - let controllerFactory; + buildSubTree(routeTree: Record, route: { handlers: any; segments: string[] }) { + let handlers = route.handlers; + let owner = this.namespace.owner; + let subTree = routeTree; + let item; + let routeClassName; + let routeHandler; + let controllerName; + let controllerClassName; + let templateName; + let controllerFactory; - for (let i = 0; i < handlers.length; i++) { - item = handlers[i]; - let handler = item.handler; - if (handler.match(/(loading|error)$/)) { - // make sure it has been defined before calling `getHandler` because - // we don't want to generate sub routes as this has side-effects. - if (!routeHasBeenDefined(owner, handler)) { - continue; + for (let i = 0; i < handlers.length; i++) { + item = handlers[i]; + let handler = item.handler; + if (handler.match(/(loading|error)$/)) { + // make sure it has been defined before calling `getHandler` because + // we don't want to generate sub routes as this has side-effects. + if (!routeHasBeenDefined(owner, handler)) { + continue; + } } - } - if (subTree[handler] === undefined) { - routeClassName = this.getClassName(handler, 'route'); + if (subTree[handler] === undefined) { + routeClassName = this.getClassName(handler, 'route'); - const router = this.router; - const routerLib = router._routerMicrolib || router.router; - // 3.9.0 removed intimate APIs from router - // https://github.com/emberjs/ember.js/pull/17843 - // https://deprecations.emberjs.com/v3.x/#toc_remove-handler-infos - if (compareVersion(VERSION, '3.9.0') !== -1) { - // Ember >= 3.9.0 + const router = this.router; + const routerLib = router._routerMicrolib || router.router; + // 3.9.0 removed intimate APIs from router + // https://github.com/emberjs/ember.js/pull/17843 + // https://deprecations.emberjs.com/v3.x/#toc_remove-handler-infos routeHandler = routerLib.getRoute(handler); - } else { - // Ember < 3.9.0 - routeHandler = routerLib.getHandler(handler); - } - // Skip when route is an unresolved promise - if (typeof routeHandler?.then === 'function') { - // ensure we rebuild the route tree when this route is resolved - routeHandler.then(() => (this._cachedRouteTree = null)); - controllerName = '(unresolved)'; - controllerClassName = '(unresolved)'; - templateName = '(unresolved)'; - } else { - const get = - routeHandler.get || - function (prop) { - return this[prop]; - }; - controllerName = get.call(routeHandler, 'controllerName') || routeHandler.routeName; - controllerFactory = owner.factoryFor - ? owner.factoryFor(`controller:${controllerName}`) - : owner._lookupFactory(`controller:${controllerName}`); - controllerClassName = this.getClassName(controllerName, 'controller'); - templateName = this.getClassName(handler, 'template'); - } + // Skip when route is an unresolved promise + if (typeof routeHandler?.then === 'function') { + // ensure we rebuild the route tree when this route is resolved + routeHandler.then(() => (this._cachedRouteTree = null)); + controllerName = '(unresolved)'; + controllerClassName = '(unresolved)'; + templateName = '(unresolved)'; + } else { + const get = + routeHandler.get || + function (this: any, prop: any) { + return this[prop]; + }; + controllerName = get.call(routeHandler, 'controllerName') || routeHandler.routeName; + controllerFactory = owner.factoryFor + ? owner.factoryFor(`controller:${controllerName}`) + : owner._lookupFactory(`controller:${controllerName}`); + controllerClassName = this.getClassName(controllerName, 'controller'); + templateName = this.getClassName(handler, 'template'); + } - subTree[handler] = { - value: { - name: handler, - routeHandler: { - className: routeClassName, + subTree[handler] = { + value: { name: handler, + routeHandler: { + className: routeClassName, + name: handler, + }, + controller: { + className: controllerClassName, + name: controllerName, + exists: Boolean(controllerFactory), + }, + template: { + name: templateName, + }, }, - controller: { - className: controllerClassName, - name: controllerName, - exists: Boolean(controllerFactory), - }, - template: { - name: templateName, - }, - }, - }; + }; - if (i === handlers.length - 1) { - // it is a route, get url - subTree[handler].value.url = getURL(owner, route.segments); - subTree[handler].value.type = 'route'; - } else { - // it is a resource, set children object - subTree[handler].children = {}; - subTree[handler].value.type = 'resource'; + if (i === handlers.length - 1) { + // it is a route, get url + subTree[handler].value.url = getURL(owner, route.segments); + subTree[handler].value.type = 'route'; + } else { + // it is a resource, set children object + subTree[handler].children = {}; + subTree[handler].value.type = 'resource'; + } } + subTree = subTree[handler].children; } - subTree = subTree[handler].children; } } -function arrayizeChildren(routeTree) { - let obj = {}; +function arrayizeChildren(routeTree: { value?: any; children: Record }) { + let obj: any = {}; // Top node doesn't have a value if (routeTree.value) { obj.value = routeTree.value; @@ -278,9 +263,9 @@ function arrayizeChildren(routeTree) { * @param {*} segments * @return {String} */ -function getURL(container, segments) { +function getURL(container: any, segments: any) { const locationImplementation = container.lookup('router:main').location; - let url = []; + let url: string[] = []; for (let i = 0; i < segments.length; i++) { let name = null; @@ -302,16 +287,16 @@ function getURL(container, segments) { } } - url = url.join('/'); + let fullUrl = url.join('/'); - if (url.match(/_unused_dummy_/)) { - url = ''; + if (fullUrl.match(/_unused_dummy_/)) { + fullUrl = ''; } else { - url = `/${url}`; - url = locationImplementation.formatURL(url); + fullUrl = `/${fullUrl}`; + fullUrl = locationImplementation.formatURL(fullUrl); } - return url; + return fullUrl; } /** @@ -320,6 +305,6 @@ function getURL(container, segments) { * @param {String} name * @return {Void} */ -function routeHasBeenDefined(owner, name) { +function routeHasBeenDefined(owner: any, name: string) { return owner.hasRegistration(`template:${name}`) || owner.hasRegistration(`route:${name}`); } diff --git a/packages/@ember/debug/ember-inspector-support/utils/bound-method.ts b/packages/@ember/debug/ember-inspector-support/utils/bound-method.ts index 4c836709445..5b33f10a438 100644 --- a/packages/@ember/debug/ember-inspector-support/utils/bound-method.ts +++ b/packages/@ember/debug/ember-inspector-support/utils/bound-method.ts @@ -1,6 +1,6 @@ const ENTRIES = new WeakMap(); -function entriesFor(obj) { +function entriesFor(obj: any) { let entries = ENTRIES.get(obj); if (entries === undefined) { @@ -19,7 +19,7 @@ function entriesFor(obj) { * @param {String|Symbol|Function} method * @return {Function} */ -export default function bound(obj, method) { +export default function bound(obj: any, method: string | Function) { let func; if (typeof method === 'function') { diff --git a/packages/@ember/debug/ember-inspector-support/utils/classify.ts b/packages/@ember/debug/ember-inspector-support/utils/classify.ts index 4c3b4a95dbd..77200195237 100644 --- a/packages/@ember/debug/ember-inspector-support/utils/classify.ts +++ b/packages/@ember/debug/ember-inspector-support/utils/classify.ts @@ -1,20 +1,24 @@ -const STRING_CLASSIFY_REGEXP_1 = /^(\-|_)+(.)?/; -const STRING_CLASSIFY_REGEXP_2 = /(.)(\-|\_|\.|\s)+(.)?/g; +const STRING_CLASSIFY_REGEXP_1 = /^([-_])+(.)?/; +const STRING_CLASSIFY_REGEXP_2 = /(.)(-|_|\.|\s)+(.)?/g; const STRING_CLASSIFY_REGEXP_3 = /(^|\/|\.)([a-z])/g; -export default function classify(str) { - let replace1 = (_match, _separator, chr) => (chr ? `_${chr.toUpperCase()}` : ''); - let replace2 = (_match, initialChar, _separator, chr) => +export default function classify(str: string) { + let replace1 = (_match: string, _separator: string, chr?: string) => + chr ? `_${chr.toUpperCase()}` : ''; + let replace2 = (_match: string, initialChar: string, _separator: string, chr?: string) => initialChar + (chr ? chr.toUpperCase() : ''); let parts = str.split('/'); for (let i = 0; i < parts.length; i++) { - parts[i] = parts[i] - .replace(STRING_CLASSIFY_REGEXP_1, replace1) - .replace(STRING_CLASSIFY_REGEXP_2, replace2); + parts[i] = parts[i]!.replace(STRING_CLASSIFY_REGEXP_1, replace1).replace( + STRING_CLASSIFY_REGEXP_2, + replace2 + ); } return parts .join('/') - .replace(STRING_CLASSIFY_REGEXP_3, (match /*, separator, chr */) => match.toUpperCase()); + .replace(STRING_CLASSIFY_REGEXP_3, (match: string /*, separator, chr */) => + match.toUpperCase() + ); } diff --git a/packages/@ember/debug/ember-inspector-support/utils/dasherize.ts b/packages/@ember/debug/ember-inspector-support/utils/dasherize.ts index 5ef13a3affa..f37cf15b944 100644 --- a/packages/@ember/debug/ember-inspector-support/utils/dasherize.ts +++ b/packages/@ember/debug/ember-inspector-support/utils/dasherize.ts @@ -1,5 +1,5 @@ const STRING_DASHERIZE_REGEXP = /[ _]/g; -export default function dasherize(str) { +export default function dasherize(str: string) { return str.replace(STRING_DASHERIZE_REGEXP, '-'); } diff --git a/packages/@ember/debug/ember-inspector-support/utils/ember/loader.ts b/packages/@ember/debug/ember-inspector-support/utils/ember/loader.ts deleted file mode 100644 index abe227feaea..00000000000 --- a/packages/@ember/debug/ember-inspector-support/utils/ember/loader.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Ember from '../ember'; - -const require = function (id) { - try { - return Ember.__loader.require(id); - } catch (e) { - return requireModule(id); - } -}; - -export function emberSafeRequire(id) { - try { - return require(id); - } catch (e) { - return undefined; - } -} - -export let EmberLoader = { - require, - requireModule: require, -}; diff --git a/packages/@ember/debug/ember-inspector-support/utils/get-object-name.ts b/packages/@ember/debug/ember-inspector-support/utils/get-object-name.ts index 71849798234..84b27927e04 100644 --- a/packages/@ember/debug/ember-inspector-support/utils/get-object-name.ts +++ b/packages/@ember/debug/ember-inspector-support/utils/get-object-name.ts @@ -1,6 +1,6 @@ import emberNames from './ember-object-names'; -export default function getObjectName(object: any) { +export default function getObjectName(object: any): string { let name = ''; let className = (object.constructor && (emberNames.get(object.constructor) || object.constructor.name)) || ''; diff --git a/packages/@ember/debug/ember-inspector-support/utils/on-ready.ts b/packages/@ember/debug/ember-inspector-support/utils/on-ready.ts index 0aeaef44491..d2731015cad 100644 --- a/packages/@ember/debug/ember-inspector-support/utils/on-ready.ts +++ b/packages/@ember/debug/ember-inspector-support/utils/on-ready.ts @@ -1,4 +1,4 @@ -export function onReady(callback) { +export function onReady(callback: () => void) { if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(completed); } else { diff --git a/packages/@ember/debug/ember-inspector-support/view-debug.ts b/packages/@ember/debug/ember-inspector-support/view-debug.ts index a1cda82cebd..fd7726000e1 100644 --- a/packages/@ember/debug/ember-inspector-support/view-debug.ts +++ b/packages/@ember/debug/ember-inspector-support/view-debug.ts @@ -4,7 +4,11 @@ import RenderTree from '@ember/debug/ember-inspector-support/libs/render-tree'; import ViewInspection from '@ember/debug/ember-inspector-support/libs/view-inspection'; import bound from '@ember/debug/ember-inspector-support/utils/bound-method'; -export default class extends DebugPort { +export default class ViewDebug extends DebugPort { + viewInspection!: ViewInspection; + renderTree!: RenderTree; + private scheduledSendTree: number | null = null; + lastRightClicked: any; get adapter() { return this.namespace?.adapter; } @@ -16,19 +20,19 @@ export default class extends DebugPort { this.prototype.portNamespace = 'view'; this.prototype.messages = { - getTree({ immediate }) { + getTree(this: ViewDebug, { immediate }: { immediate: boolean }) { this.sendTree(immediate); }, - showInspection({ id, pin }) { + showInspection(this: ViewDebug, { id, pin }: { id: string; pin: boolean }) { this.viewInspection.show(id, pin); }, - hideInspection() { + hideInspection(this: ViewDebug) { this.viewInspection.hide(); }, - inspectViews({ inspect }) { + inspectViews(this: ViewDebug, { inspect }: { inspect: boolean }) { if (inspect) { this.startInspecting(); } else { @@ -36,15 +40,15 @@ export default class extends DebugPort { } }, - scrollIntoView({ id }) { + scrollIntoView(this: ViewDebug, { id }: { id: string }) { this.renderTree.scrollIntoView(id); }, - inspectElement({ id }) { + inspectElement(this: ViewDebug, { id }: { id: string }) { this.renderTree.inspectElement(id); }, - contextMenu() { + contextMenu(this: ViewDebug) { let { lastRightClicked } = this; this.lastRightClicked = null; this.inspectNearest(lastRightClicked); @@ -93,7 +97,7 @@ export default class extends DebugPort { } } - onRightClick(event) { + onRightClick(event: MouseEvent) { if (event.button === 2) { this.lastRightClicked = event.target; } @@ -103,7 +107,7 @@ export default class extends DebugPort { // TODO hide or redraw highlight/tooltip } - inspectNearest(node) { + inspectNearest(node: Node) { let renderNode = this.viewInspection.inspectNearest(node); if (!renderNode) { @@ -125,13 +129,13 @@ export default class extends DebugPort { * @method inspectNode * @param {Node} node The DOM node to inspect */ - inspectNode(node) { + inspectNode(node: Node) { this.adapter.inspectValue(node); } sendTree(immediate = false) { if (immediate) { - this.send(true); + this.send(); return; } @@ -163,7 +167,7 @@ export default class extends DebugPort { this.viewInspection.stop(); } - didShowInspection(id, pin) { + didShowInspection(id: string, pin: boolean) { if (pin) { this.sendMessage('inspectComponent', { id }); } else { @@ -171,7 +175,7 @@ export default class extends DebugPort { } } - didHideInspection(id, pin) { + didHideInspection(id: string, pin: boolean) { this.sendMessage('cancelSelection', { id, pin }); }