@memlab/lens 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/dist/config/config.d.ts +22 -0
  3. package/dist/core/dom-observer.d.ts +10 -0
  4. package/dist/core/event-listener-tracker.d.ts +33 -0
  5. package/dist/core/react-fiber-analysis.d.ts +4 -0
  6. package/dist/core/react-memory-scan.d.ts +36 -0
  7. package/{src/core/types.ts → dist/core/types.d.ts} +56 -80
  8. package/{src/index.ts → dist/core/valid-component-name.d.ts} +2 -7
  9. package/dist/extensions/basic-extension.d.ts +30 -0
  10. package/dist/extensions/dom-visualization-extension.d.ts +17 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/memlens.lib.bundle.js +1695 -0
  13. package/dist/memlens.lib.bundle.min.js +1 -0
  14. package/dist/memlens.lib.d.ts +12 -0
  15. package/dist/memlens.run.bundle.js +2673 -0
  16. package/dist/memlens.run.bundle.min.js +1 -0
  17. package/dist/memlens.run.d.ts +1 -0
  18. package/dist/tests/bundle/lib.bundle.test.d.ts +1 -0
  19. package/dist/tests/bundle/run.bundle.start.head.test.d.ts +1 -0
  20. package/dist/tests/bundle/run.bundle.start.test.d.ts +1 -0
  21. package/dist/tests/bundle/run.bundle.test.d.ts +1 -0
  22. package/dist/tests/fiber/dev.fiber.complex.dev.test.d.ts +1 -0
  23. package/dist/tests/fiber/dev.fiber.complex.list.dev.test.d.ts +1 -0
  24. package/dist/tests/fiber/dev.fiber.complex.prod.test.d.ts +1 -0
  25. package/dist/tests/fiber/dev.fiber.name.dev.test.d.ts +1 -0
  26. package/dist/tests/fiber/dev.fiber.name.prod.test.d.ts +1 -0
  27. package/dist/tests/utils/test-utils.d.ts +11 -0
  28. package/dist/utils/intersection-observer.d.ts +18 -0
  29. package/dist/utils/react-fiber-utils.d.ts +56 -0
  30. package/dist/utils/utils.d.ts +26 -0
  31. package/dist/utils/weak-ref-utils.d.ts +67 -0
  32. package/dist/visual/components/component-stack-panel.d.ts +11 -0
  33. package/dist/visual/components/control-widget.d.ts +13 -0
  34. package/dist/visual/components/overlay-rectangle.d.ts +11 -0
  35. package/dist/visual/components/status-text.d.ts +2 -0
  36. package/dist/visual/components/toggle-button.d.ts +3 -0
  37. package/dist/visual/components/visual-overlay.d.ts +1 -0
  38. package/dist/visual/dom-element-visualizer-interactive.d.ts +26 -0
  39. package/{src/core/valid-component-name.ts → dist/visual/dom-element-visualizer.d.ts} +5 -7
  40. package/dist/visual/visual-utils.d.ts +16 -0
  41. package/package.json +5 -1
  42. package/explainer.md +0 -54
  43. package/playwright.config.ts +0 -21
  44. package/src/config/config.ts +0 -32
  45. package/src/core/dom-observer.ts +0 -189
  46. package/src/core/event-listener-tracker.ts +0 -171
  47. package/src/core/react-fiber-analysis.ts +0 -123
  48. package/src/core/react-memory-scan.ts +0 -366
  49. package/src/extensions/basic-extension.ts +0 -41
  50. package/src/extensions/dom-visualization-extension.ts +0 -42
  51. package/src/memlens.lib.js.flow +0 -75
  52. package/src/memlens.lib.ts +0 -22
  53. package/src/memlens.run.ts +0 -21
  54. package/src/tests/bundle/lib.bundle.test.ts +0 -31
  55. package/src/tests/bundle/run.bundle.start.head.test.ts +0 -48
  56. package/src/tests/bundle/run.bundle.start.test.ts +0 -48
  57. package/src/tests/bundle/run.bundle.test.ts +0 -51
  58. package/src/tests/fiber/dev.fiber.complex.dev.test.ts +0 -92
  59. package/src/tests/fiber/dev.fiber.complex.list.dev.test.ts +0 -118
  60. package/src/tests/fiber/dev.fiber.complex.prod.test.ts +0 -92
  61. package/src/tests/fiber/dev.fiber.name.dev.test.ts +0 -77
  62. package/src/tests/fiber/dev.fiber.name.prod.test.ts +0 -79
  63. package/src/tests/lib/babel.prod.js +0 -4
  64. package/src/tests/lib/react-dom-v18.dev.js +0 -29926
  65. package/src/tests/lib/react-dom-v18.prod.js +0 -269
  66. package/src/tests/lib/react-v18.dev.js +0 -3345
  67. package/src/tests/lib/react-v18.prod.js +0 -33
  68. package/src/tests/manual/playwright-open-manual.js +0 -40
  69. package/src/tests/manual/todo-list/todo-with-run.bundle.html +0 -80
  70. package/src/tests/utils/test-utils.ts +0 -28
  71. package/src/utils/intersection-observer.ts +0 -65
  72. package/src/utils/react-fiber-utils.ts +0 -212
  73. package/src/utils/utils.ts +0 -201
  74. package/src/utils/weak-ref-utils.ts +0 -308
  75. package/src/visual/components/component-stack-panel.ts +0 -85
  76. package/src/visual/components/control-widget.ts +0 -96
  77. package/src/visual/components/overlay-rectangle.ts +0 -167
  78. package/src/visual/components/status-text.ts +0 -53
  79. package/src/visual/components/toggle-button.ts +0 -57
  80. package/src/visual/components/visual-overlay.ts +0 -19
  81. package/src/visual/dom-element-visualizer-interactive.ts +0 -358
  82. package/src/visual/dom-element-visualizer.ts +0 -130
  83. package/src/visual/visual-utils.ts +0 -89
  84. package/tsconfig.json +0 -18
  85. package/webpack.config.js +0 -105
@@ -1,57 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @format
8
- * @oncall memory_lab
9
- */
10
- import {createVisualizerElement} from '../visual-utils';
11
-
12
- export function createToggleButton(
13
- overlayDiv: HTMLDivElement,
14
- hideAllRef: {value: boolean},
15
- ): HTMLDivElement {
16
- const toggleWrapper = createVisualizerElement('div') as HTMLDivElement;
17
- toggleWrapper.style.width = '40px';
18
- toggleWrapper.style.height = '24px';
19
- toggleWrapper.style.borderRadius = '9999px';
20
- toggleWrapper.style.backgroundColor = '#34C759'; // ON by default
21
- toggleWrapper.style.cursor = 'pointer';
22
- toggleWrapper.style.position = 'relative';
23
- toggleWrapper.style.transition = 'background-color 0.3s ease';
24
-
25
- const knob = createVisualizerElement('div');
26
- knob.style.width = '18px';
27
- knob.style.height = '18px';
28
- knob.style.backgroundColor = 'white';
29
- knob.style.borderRadius = '50%';
30
- knob.style.position = 'absolute';
31
- knob.style.top = '3px';
32
- knob.style.left = '3px'; // ON position
33
- knob.style.transition = 'left 0.25s ease';
34
- toggleWrapper.appendChild(knob);
35
-
36
- toggleWrapper.addEventListener('click', () => {
37
- hideAllRef.value = !hideAllRef.value;
38
-
39
- if (hideAllRef.value) {
40
- // OFF state
41
- overlayDiv.style.display = 'none';
42
- overlayDiv.style.pointerEvents = 'none';
43
- overlayDiv.style.userSelect = 'none';
44
- knob.style.left = '19px';
45
- toggleWrapper.style.backgroundColor = '#FF3B30'; // Apple red
46
- } else {
47
- // ON state
48
- overlayDiv.style.display = 'block';
49
- overlayDiv.style.pointerEvents = 'auto';
50
- overlayDiv.style.userSelect = 'auto';
51
- knob.style.left = '3px';
52
- toggleWrapper.style.backgroundColor = '#34C759'; // Apple green
53
- }
54
- });
55
-
56
- return toggleWrapper;
57
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @format
8
- * @oncall memory_lab
9
- */
10
- import {createVisualizerElement} from '../visual-utils';
11
-
12
- export function createOverlayDiv(): HTMLDivElement {
13
- const overlayDiv = createVisualizerElement('div') as HTMLDivElement;
14
- overlayDiv.style.position = 'absolute';
15
- overlayDiv.style.top = '0px';
16
- overlayDiv.style.left = '0px';
17
- overlayDiv.id = 'memory-visualization-overlay';
18
- return overlayDiv;
19
- }
@@ -1,358 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @format
8
- * @oncall memory_lab
9
- */
10
- import type {DOMElementInfo, AnyValue, Optional, Nullable} from '../core/types';
11
- import DOMElementVisualizer from './dom-element-visualizer';
12
- import {createOverlayDiv} from './components/visual-overlay';
13
- import {createControlWidget} from './components/control-widget';
14
- import {createOverlayRectangle} from './components/overlay-rectangle';
15
- import {getDOMElementCount} from '../utils/utils';
16
- import {tryToAttachOverlay} from './visual-utils';
17
-
18
- type ElementVisualizer = {
19
- elementInfo: DOMElementInfo;
20
- visualizerElementRef: WeakRef<Element>;
21
- };
22
-
23
- export type VisualizerData = {
24
- detachedDOMElementsCount: number;
25
- totalDOMElementsCount: number;
26
- selectedElementId: Nullable<number>;
27
- pinnedElementId: Nullable<number>;
28
- selectedReactComponentStack: Array<string>;
29
- setPinnedElementId: (elementId: Nullable<number>) => void;
30
- };
31
-
32
- export type DateUpdateCallback = (data: VisualizerData) => void;
33
- export type RegisterDataUpdateCallback = (cb: DateUpdateCallback) => void;
34
-
35
- export default class DOMElementVisualizerInteractive extends DOMElementVisualizer {
36
- #elementIdToRectangle: Map<number, ElementVisualizer>;
37
- #visualizationOverlayDiv: HTMLDivElement;
38
- #controlWidget: HTMLDivElement;
39
- #blockedElementIds: Set<number>;
40
- #currentVisualData: VisualizerData;
41
- #hideAllRef: {value: boolean};
42
- #updateDataCallbacks: Array<DateUpdateCallback> = [];
43
-
44
- constructor() {
45
- super();
46
- this.#hideAllRef = {value: false};
47
- this.#visualizationOverlayDiv = createOverlayDiv();
48
- tryToAttachOverlay(this.#visualizationOverlayDiv);
49
- this.#controlWidget = createControlWidget(
50
- this.#visualizationOverlayDiv,
51
- this.#hideAllRef,
52
- cb => {
53
- this.#updateDataCallbacks.push(cb);
54
- },
55
- );
56
- tryToAttachOverlay(this.#controlWidget);
57
- this.#elementIdToRectangle = new Map();
58
- this.#currentVisualData = this.#initVisualizerData();
59
- this.#blockedElementIds = new Set();
60
- this.#listenToKeyboardEvent();
61
- }
62
-
63
- #listenToKeyboardEvent() {
64
- document.addEventListener('keydown', event => {
65
- if (event.key === 'd' || event.key === 'D') {
66
- if (this.#currentVisualData.selectedElementId != null) {
67
- this.#blockedElementIds.add(
68
- this.#currentVisualData.selectedElementId,
69
- );
70
- }
71
- }
72
- });
73
- }
74
-
75
- #initVisualizerData(): VisualizerData {
76
- const data: VisualizerData = {
77
- detachedDOMElementsCount: 0,
78
- totalDOMElementsCount: getDOMElementCount(),
79
- selectedElementId: null,
80
- selectedReactComponentStack: [],
81
- pinnedElementId: null,
82
- setPinnedElementId: (pinnedElementId: Nullable<number>) => {
83
- if (data.pinnedElementId == pinnedElementId) {
84
- return;
85
- }
86
- // unpin the original pinned element
87
- const oldPin = this.#getOutlineElementByElementId(data.pinnedElementId);
88
- (oldPin as AnyValue)?.__unpinned?.();
89
- // pin the newly pinned element
90
- const newPin = this.#getOutlineElementByElementId(pinnedElementId);
91
- (newPin as AnyValue)?.__pinned?.();
92
- data.pinnedElementId = pinnedElementId;
93
- },
94
- };
95
- return data;
96
- }
97
-
98
- #getOutlineElementByElementId(
99
- elementId: Nullable<number>,
100
- ): Optional<Element> {
101
- if (elementId == null) {
102
- return null;
103
- }
104
- const info = this.#elementIdToRectangle.get(elementId);
105
- if (info == null) {
106
- return null;
107
- }
108
- return info.visualizerElementRef.deref();
109
- }
110
-
111
- #getElementIdSet(domElementInfoList: Array<DOMElementInfo>): Set<number> {
112
- const elementIdSet = new Set<number>();
113
- for (const info of domElementInfoList) {
114
- const element = info.element.deref() as AnyValue;
115
- if (element == null) {
116
- continue;
117
- }
118
- const elementId = element.detachedElementId;
119
- if (elementId == null) {
120
- continue;
121
- }
122
- elementIdSet.add(elementId);
123
- }
124
- return elementIdSet;
125
- }
126
-
127
- #getComponentStackForElement(
128
- elementId: Optional<number>,
129
- ): ElementVisualizer[] {
130
- const ret: ElementVisualizer[] = [];
131
- if (elementId == null) {
132
- return ret;
133
- }
134
- const visualizer = this.#elementIdToRectangle.get(elementId);
135
- if (visualizer == null) {
136
- return ret;
137
- }
138
- const element = visualizer.elementInfo.element.deref() as AnyValue;
139
- if (element == null) {
140
- return ret;
141
- }
142
- // traverse parent elements
143
- let currentElement: Optional<Element> = element;
144
- while (currentElement) {
145
- if (currentElement.isConnected) {
146
- break;
147
- }
148
- const elementId = (currentElement as AnyValue).detachedElementId;
149
- const info = this.#elementIdToRectangle.get(elementId);
150
- if (info == null) {
151
- break;
152
- }
153
- ret.push(info);
154
- currentElement = currentElement.parentElement;
155
- }
156
- return ret;
157
- }
158
-
159
- #traverseUpOutlineElements(
160
- elementId: number,
161
- callback: (element: HTMLElement) => void,
162
- ): void {
163
- const visualizer = this.#elementIdToRectangle.get(elementId);
164
- if (visualizer == null) {
165
- return;
166
- }
167
- const visitedElementIds = new Set<number>();
168
- let currentElement: Optional<Element> =
169
- visualizer.elementInfo.element.deref();
170
- while (currentElement) {
171
- if (currentElement.isConnected) {
172
- break;
173
- }
174
- const elementIdStr = (currentElement as AnyValue).detachedElementId;
175
- const elementId = parseInt(elementIdStr, 10);
176
- if (visitedElementIds.has(elementId)) {
177
- break;
178
- }
179
- visitedElementIds.add(elementId);
180
- const visualizerInfo = this.#elementIdToRectangle.get(elementId);
181
- if (visualizerInfo == null) {
182
- break;
183
- }
184
- const visualizerElement = visualizerInfo.visualizerElementRef.deref();
185
- if (visualizerElement == null) {
186
- break;
187
- }
188
- callback(visualizerElement as HTMLElement);
189
- currentElement = currentElement.parentElement;
190
- }
191
- }
192
-
193
- #removeVisualizerElement(elementId: number) {
194
- const visualizer = this.#elementIdToRectangle.get(elementId);
195
- this.#elementIdToRectangle.delete(elementId);
196
- if (visualizer == null) {
197
- return;
198
- }
199
- const visualizerElement = visualizer.visualizerElementRef.deref();
200
- if (visualizerElement == null) {
201
- return;
202
- }
203
- // invoke the overlay specific code to clean
204
- (visualizerElement as AnyValue)?.__cleanup?.();
205
- visualizerElement.remove();
206
- }
207
-
208
- #cleanup(domElementInfoList: Array<DOMElementInfo>) {
209
- // first pass remove all those painted visualizer for elements
210
- // that either 1) is GCed or 2) is connected to the DOM tree
211
- for (const [
212
- elementId,
213
- elementVistualizer,
214
- ] of this.#elementIdToRectangle.entries()) {
215
- if (elementId == null) {
216
- continue;
217
- }
218
- const element = elementVistualizer.elementInfo.element.deref();
219
- if (element == null) {
220
- this.#removeVisualizerElement(elementId);
221
- }
222
- }
223
-
224
- // second pass remove all those outlines that won't be painted later
225
- const willPaintElementIdSet = this.#getElementIdSet(domElementInfoList);
226
- for (const [elementId] of this.#elementIdToRectangle.entries()) {
227
- if (elementId == null) {
228
- continue;
229
- }
230
- if (
231
- willPaintElementIdSet.has(elementId) &&
232
- !this.#blockedElementIds.has(elementId)
233
- ) {
234
- continue;
235
- }
236
- this.#removeVisualizerElement(elementId);
237
- }
238
- }
239
-
240
- #getZIndex(info: DOMElementInfo): number {
241
- const zIndexBase = 9999;
242
- const element = info.element.deref() as AnyValue;
243
- if (element == null) {
244
- return 0;
245
- }
246
- const elementId = element.detachedElementId;
247
- if (elementId == null) {
248
- return 0;
249
- }
250
- const rectangle = info.boundingRect ?? {width: 50, height: 50};
251
- let ret = zIndexBase;
252
- // element with a higher element id (created later) has a higher z-index
253
- ret += parseInt(elementId, 10);
254
- // element with bigger area will have a lower z-index
255
- ret += Math.floor(10000000 / (rectangle.width * rectangle.height));
256
- return ret;
257
- }
258
-
259
- #paint(domElementInfoList: Array<DOMElementInfo>) {
260
- for (const info of domElementInfoList) {
261
- const element = info.element.deref() as AnyValue;
262
- if (element == null) {
263
- continue;
264
- }
265
- const elementId = element.detachedElementId;
266
- if (elementId == null) {
267
- continue;
268
- }
269
- if (this.#blockedElementIds.has(elementId)) {
270
- continue;
271
- }
272
- if (this.#elementIdToRectangle.has(elementId)) {
273
- continue;
274
- }
275
- if (element == null) {
276
- continue;
277
- }
278
- if (element.isConnected) {
279
- continue;
280
- }
281
- const zIndex = this.#getZIndex(info);
282
- const visualizerElementRef = createOverlayRectangle(
283
- elementId,
284
- info,
285
- this.#visualizationOverlayDiv,
286
- (selectedId: number | null) => {
287
- this.#currentVisualData.selectedElementId = selectedId;
288
- this.#updateVisualizerData();
289
- if (selectedId == null) {
290
- return;
291
- }
292
- this.#traverseUpOutlineElements(selectedId, element => {
293
- (element as AnyValue)?.__selected?.();
294
- });
295
- },
296
- (unselectedId: number | null) => {
297
- if (this.#currentVisualData.selectedElementId === unselectedId) {
298
- this.#currentVisualData.selectedElementId = null;
299
- this.#updateVisualizerData();
300
- }
301
- if (unselectedId == null) {
302
- return;
303
- }
304
- this.#traverseUpOutlineElements(unselectedId, element => {
305
- (element as AnyValue)?.__unselected?.();
306
- });
307
- },
308
- (clickedId: Nullable<number>) => {
309
- if (this.#currentVisualData.pinnedElementId === clickedId) {
310
- this.#currentVisualData.setPinnedElementId(null);
311
- } else {
312
- this.#currentVisualData.setPinnedElementId(clickedId);
313
- }
314
- this.#updateVisualizerData();
315
- },
316
- zIndex,
317
- );
318
- if (
319
- visualizerElementRef == null ||
320
- visualizerElementRef.deref() == null
321
- ) {
322
- return null;
323
- }
324
- const visualizer = {
325
- elementInfo: info,
326
- visualizerElementRef,
327
- };
328
- this.#elementIdToRectangle.set(elementId, visualizer);
329
- }
330
- }
331
-
332
- #updateVisualizerData() {
333
- const data = this.#currentVisualData;
334
- if (data.pinnedElementId != null) {
335
- data.selectedElementId = data.pinnedElementId;
336
- }
337
- data.detachedDOMElementsCount = this.#elementIdToRectangle.size;
338
- data.totalDOMElementsCount = getDOMElementCount();
339
- const selectedElementInfo = this.#elementIdToRectangle.get(
340
- data.selectedElementId ?? -1,
341
- )?.elementInfo;
342
- data.selectedReactComponentStack =
343
- selectedElementInfo?.componentStack ?? [];
344
- for (const cb of this.#updateDataCallbacks) {
345
- cb({...this.#currentVisualData});
346
- }
347
- }
348
-
349
- repaint(domElementInfoList: Array<DOMElementInfo>) {
350
- // this.#controlWidget.remove();
351
- this.#visualizationOverlayDiv.remove();
352
- this.#cleanup(domElementInfoList);
353
- this.#paint(domElementInfoList);
354
- this.#updateVisualizerData();
355
- tryToAttachOverlay(this.#visualizationOverlayDiv);
356
- // tryToAttachOverlay(this.#controlWidget);
357
- }
358
- }
@@ -1,130 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @format
8
- * @oncall memory_lab
9
- */
10
- import type {DOMElementInfo, Nullable, AnyValue} from '../core/types';
11
- import {createVisualizerElement} from './visual-utils';
12
-
13
- export default class DOMElementVisualizer {
14
- #canvas: Nullable<HTMLCanvasElement>;
15
- constructor() {
16
- this.#canvas = null;
17
- }
18
-
19
- #paint(domElementInfoList: Array<DOMElementInfo>) {
20
- if (!this.#canvas) {
21
- const canvas = this.#createAndAppendCanvas();
22
- this.#canvas = canvas;
23
- }
24
- this.#paintRectangles(domElementInfoList);
25
- }
26
-
27
- #cleanupExistingCanvas() {
28
- // Clean up any existing canvas
29
- if (this.#canvas) {
30
- const ctx = this.#canvas.getContext('2d');
31
- ctx?.clearRect(0, 0, this.#canvas.width, this.#canvas.height);
32
- this.#canvas.remove();
33
- this.#canvas = null;
34
- }
35
- }
36
-
37
- #tryToAttachCanvas(canvas: Element) {
38
- if (document.body) {
39
- document.body.appendChild(canvas);
40
- }
41
- }
42
-
43
- #createAndAppendCanvas() {
44
- // Create and insert the canvas element
45
- const canvas = createVisualizerElement('canvas') as HTMLCanvasElement;
46
- canvas.id = 'overlayCanvas';
47
- this.#tryToAttachCanvas(canvas);
48
-
49
- // Style the canvas to cover the entire page
50
- canvas.style.position = 'absolute';
51
- canvas.style.top = '0';
52
- canvas.style.left = '0';
53
- canvas.style.width = '100%';
54
- canvas.style.height = '100%';
55
- canvas.style.pointerEvents = 'none';
56
- canvas.style.zIndex = '99999';
57
-
58
- // Set canvas dimensions to match the window dimensions
59
- canvas.width = window.innerWidth;
60
- canvas.height = window.innerHeight;
61
- return canvas;
62
- }
63
-
64
- #paintKey(info: DOMElementInfo): string {
65
- const {boundingRect} = info;
66
- return JSON.stringify({boundingRect});
67
- }
68
-
69
- #paintRectangles(domElementInfoList: Array<DOMElementInfo>) {
70
- const canvas = this.#canvas;
71
- if (!canvas) {
72
- return;
73
- }
74
- // Get the 2D drawing context
75
- const ctx = canvas.getContext('2d');
76
- if (ctx == null) {
77
- return;
78
- }
79
-
80
- // Set rectangle styles
81
- ctx.strokeStyle = 'rgba(75, 192, 192, 0.8)';
82
- ctx.lineWidth = 2;
83
- ctx.fillStyle = 'rgba(75, 192, 192, 0.05)';
84
- ctx.font = '11px Inter, system-ui, -apple-system, sans-serif';
85
-
86
- const paintedInfo = new Set<string>();
87
-
88
- // Draw the rectangles
89
- domElementInfoList.forEach((info: DOMElementInfo) => {
90
- const rect = info.boundingRect;
91
- if (rect == null) {
92
- return;
93
- }
94
- const key = this.#paintKey(info);
95
- if (paintedInfo.has(key)) {
96
- return;
97
- }
98
- paintedInfo.add(key);
99
- ctx.fillRect(rect.left, rect.top, rect.width, rect.height);
100
- ctx.strokeRect(rect.left, rect.top, rect.width, rect.height);
101
-
102
- const component = info.componentStack?.[0];
103
- if (component) {
104
- // attach detached element id key so that it is easy to search in heap snapshot
105
- const element = info.element.deref() as AnyValue;
106
- const elementId = element?.detachedElementId;
107
- const elementIdText = elementId ? ` (${elementId})` : '';
108
- const text = `${component}${elementIdText}`;
109
- ctx.fillStyle = 'rgba(74, 131, 224, 1)';
110
- ctx.fillText(text, rect.left + 5, rect.top + 15); // Draw the name
111
- ctx.fillStyle = 'rgba(75, 192, 192, 0.05)';
112
- }
113
- });
114
- }
115
-
116
- #cleanup() {
117
- const canvas = this.#canvas;
118
- if (canvas) {
119
- const ctx = canvas.getContext('2d');
120
- ctx?.clearRect(0, 0, canvas.width, canvas.height);
121
- canvas.parentNode?.removeChild(canvas);
122
- }
123
- this.#canvas = null;
124
- }
125
-
126
- repaint(domElementInfoList: Array<DOMElementInfo>) {
127
- this.#cleanup();
128
- this.#paint(domElementInfoList);
129
- }
130
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @format
8
- * @oncall memory_lab
9
- */
10
- import type {AnyValue} from '../core/types';
11
-
12
- const VISUALIZER_DATA_ATTR = 'data-visualizer';
13
-
14
- function setVisualizerElement(element: Element) {
15
- element.setAttribute(VISUALIZER_DATA_ATTR, 'true');
16
- element.setAttribute('data-visualcompletion', 'ignore');
17
- }
18
-
19
- export function isVisualizerElement(element: Element): boolean {
20
- return element.getAttribute(VISUALIZER_DATA_ATTR) === 'true';
21
- }
22
-
23
- export function createVisualizerElement(tag: string): HTMLElement {
24
- const element = document.createElement(tag);
25
- setVisualizerElement(element);
26
- return element;
27
- }
28
-
29
- export function tryToAttachOverlay(overlayDiv: HTMLDivElement) {
30
- if (document.body) {
31
- document.body.appendChild(overlayDiv);
32
- }
33
- }
34
-
35
- type EventListenerEntry = {
36
- type: string;
37
- cb: EventListenerOrEventListenerObject;
38
- options?: boolean | AddEventListenerOptions;
39
- };
40
-
41
- const listenerMap = new WeakMap<EventTarget, EventListenerEntry[]>();
42
-
43
- export function addTrackedListener(
44
- elRef: WeakRef<EventTarget>,
45
- type: string,
46
- cb: EventListenerOrEventListenerObject,
47
- options?: boolean | AddEventListenerOptions,
48
- ): void {
49
- const el = elRef.deref();
50
- if (!el) return;
51
-
52
- el.addEventListener(type, cb, options);
53
-
54
- if (!listenerMap.has(el)) {
55
- listenerMap.set(el, []);
56
- }
57
-
58
- listenerMap.get(el)?.push({type, cb, options});
59
- }
60
-
61
- export function removeAllListeners(elRef: WeakRef<EventTarget>): void {
62
- const el = elRef.deref();
63
- if (!el) return;
64
-
65
- const listeners = listenerMap.get(el);
66
- if (!listeners) return;
67
-
68
- for (const {type, cb, options} of listeners) {
69
- el.removeEventListener(type, cb, options);
70
- }
71
-
72
- listenerMap.delete(el);
73
- }
74
-
75
- export function debounce<T extends (...args: AnyValue[]) => AnyValue>(
76
- callback: T,
77
- delay: number,
78
- ): (...args: Parameters<T>) => void {
79
- let timer: ReturnType<typeof setTimeout> | null = null;
80
-
81
- return (...args: Parameters<T>) => {
82
- if (timer) {
83
- clearTimeout(timer);
84
- }
85
- timer = setTimeout(() => {
86
- callback(...args);
87
- }, delay);
88
- };
89
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES6", // Choose ES5 or ES6 for broad browser support
4
- "module": "NodeNext", // Use ESNext for modern module systems; CommonJS for Node.js
5
- "moduleResolution": "nodenext",
6
- "lib": ["DOM", "ES2021"], // Include DOM for browser types
7
- "outDir": "./dist", // Output directory for compiled files
8
- "rootDir": "./src", // Source directory for TypeScript files
9
- "strict": true, // Enable all strict type-checking options
10
- "esModuleInterop": true, // Enable interoperability between CommonJS and ES Modules
11
- "skipLibCheck": true, // Skip type-checking of declaration files for performance
12
- "forceConsistentCasingInFileNames": true, // Ensure consistent casing in imports
13
- "declaration": true, // generate .d.ts files
14
- "composite": true
15
- },
16
- "include": ["src/**/*"], // Include all files in the src directory
17
- "exclude": ["node_modules", "dist", "src/lib-index.js.flow"] // Exclude unnecessary directories
18
- }