@memlab/lens 1.0.0

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 (50) hide show
  1. package/README.md +63 -0
  2. package/dist/index.js +112 -0
  3. package/explainer.md +54 -0
  4. package/package.json +34 -0
  5. package/playwright.config.ts +21 -0
  6. package/src/config/config.ts +32 -0
  7. package/src/core/dom-observer.ts +189 -0
  8. package/src/core/event-listener-tracker.ts +171 -0
  9. package/src/core/react-fiber-analysis.ts +123 -0
  10. package/src/core/react-memory-scan.ts +366 -0
  11. package/src/core/types.ts +180 -0
  12. package/src/core/valid-component-name.ts +17 -0
  13. package/src/extensions/basic-extension.ts +41 -0
  14. package/src/extensions/dom-visualization-extension.ts +42 -0
  15. package/src/index.ts +16 -0
  16. package/src/memlens.lib.js.flow +75 -0
  17. package/src/memlens.lib.ts +22 -0
  18. package/src/memlens.run.ts +21 -0
  19. package/src/tests/bundle/lib.bundle.test.ts +31 -0
  20. package/src/tests/bundle/run.bundle.start.head.test.ts +48 -0
  21. package/src/tests/bundle/run.bundle.start.test.ts +48 -0
  22. package/src/tests/bundle/run.bundle.test.ts +51 -0
  23. package/src/tests/fiber/dev.fiber.complex.dev.test.ts +92 -0
  24. package/src/tests/fiber/dev.fiber.complex.list.dev.test.ts +118 -0
  25. package/src/tests/fiber/dev.fiber.complex.prod.test.ts +92 -0
  26. package/src/tests/fiber/dev.fiber.name.dev.test.ts +77 -0
  27. package/src/tests/fiber/dev.fiber.name.prod.test.ts +79 -0
  28. package/src/tests/lib/babel.prod.js +4 -0
  29. package/src/tests/lib/react-dom-v18.dev.js +29926 -0
  30. package/src/tests/lib/react-dom-v18.prod.js +269 -0
  31. package/src/tests/lib/react-v18.dev.js +3345 -0
  32. package/src/tests/lib/react-v18.prod.js +33 -0
  33. package/src/tests/manual/playwright-open-manual.js +40 -0
  34. package/src/tests/manual/todo-list/todo-with-run.bundle.html +80 -0
  35. package/src/tests/utils/test-utils.ts +28 -0
  36. package/src/utils/intersection-observer.ts +65 -0
  37. package/src/utils/react-fiber-utils.ts +212 -0
  38. package/src/utils/utils.ts +201 -0
  39. package/src/utils/weak-ref-utils.ts +308 -0
  40. package/src/visual/components/component-stack-panel.ts +85 -0
  41. package/src/visual/components/control-widget.ts +96 -0
  42. package/src/visual/components/overlay-rectangle.ts +167 -0
  43. package/src/visual/components/status-text.ts +53 -0
  44. package/src/visual/components/toggle-button.ts +57 -0
  45. package/src/visual/components/visual-overlay.ts +19 -0
  46. package/src/visual/dom-element-visualizer-interactive.ts +358 -0
  47. package/src/visual/dom-element-visualizer.ts +130 -0
  48. package/src/visual/visual-utils.ts +89 -0
  49. package/tsconfig.json +18 -0
  50. package/webpack.config.js +105 -0
@@ -0,0 +1,92 @@
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
+ import {test, expect} from '@playwright/test';
13
+ import path from 'path';
14
+ import fs from 'fs';
15
+ import {srcPath} from '../utils/test-utils';
16
+ import {libBundleFilePath} from '../utils/test-utils';
17
+
18
+ test('scan should identify react components in a complex fiber tree (React 18 Prod)', async ({
19
+ page,
20
+ }) => {
21
+ const bundleCode = fs.readFileSync(libBundleFilePath, 'utf8');
22
+
23
+ const reactDevPath = path.join(srcPath, 'tests', 'lib', 'react-v18.prod.js');
24
+ const reactDevCode = fs.readFileSync(reactDevPath, 'utf8');
25
+
26
+ const reactDOMDevPath = path.join(
27
+ srcPath,
28
+ 'tests',
29
+ 'lib',
30
+ 'react-dom-v18.prod.js',
31
+ );
32
+ const reactDOMDevCode = fs.readFileSync(reactDOMDevPath, 'utf8');
33
+
34
+ // Create a simple test page with React and a custom component
35
+ await page.setContent(`
36
+ <html>
37
+ <body>
38
+ <div id="root"></div>
39
+ <script>${reactDevCode}</script>
40
+ <script>${reactDOMDevCode}</script>
41
+ <script>${bundleCode}</script>
42
+ <script>
43
+ function TopContainer() {
44
+ return React.createElement(
45
+ 'div',
46
+ null,
47
+ React.createElement(TestContainer),
48
+ React.createElement(TestContainer),
49
+ React.createElement(TestContainer),
50
+ );
51
+ }
52
+ function TestContainer() {
53
+ return React.createElement(
54
+ 'div',
55
+ null,
56
+ React.createElement(TestComponent),
57
+ React.createElement(TestComponent)
58
+ );
59
+ }
60
+
61
+ function TestComponent() {
62
+ return React.createElement('div', null, 'Test Component');
63
+ }
64
+
65
+ ReactDOM.render(
66
+ React.createElement(TopContainer),
67
+ document.getElementById('root')
68
+ );
69
+ </script>
70
+ </body>
71
+ </html>
72
+ `);
73
+
74
+ // Now the global `MemLens` should be available in the page
75
+ const componentIdentified = await page.evaluate(() => {
76
+ const createReactMemoryScan = (window as AnyValue).MemLens
77
+ .createReactMemoryScan;
78
+ const instance = createReactMemoryScan();
79
+ const analysisResult = instance.scan();
80
+ const componentCountCorrect =
81
+ analysisResult.componentToFiberNodeCount.get('TestComponent') === 6;
82
+ const containerCountCorrect =
83
+ analysisResult.componentToFiberNodeCount.get('TestContainer') === 3;
84
+ const topContainerCountCorrect =
85
+ analysisResult.componentToFiberNodeCount.get('TopContainer') === 1;
86
+ return (
87
+ componentCountCorrect && containerCountCorrect && topContainerCountCorrect
88
+ );
89
+ });
90
+
91
+ expect(componentIdentified).toBe(true);
92
+ });
@@ -0,0 +1,77 @@
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
+ import {test, expect} from '@playwright/test';
13
+ import path from 'path';
14
+ import fs from 'fs';
15
+ import {srcPath} from '../utils/test-utils';
16
+ import {libBundleFilePath} from '../utils/test-utils';
17
+
18
+ test('scan should identify react components (React 18 Dev)', async ({page}) => {
19
+ const bundleCode = fs.readFileSync(libBundleFilePath, 'utf8');
20
+
21
+ const reactDevPath = path.join(srcPath, 'tests', 'lib', 'react-v18.dev.js');
22
+ const reactDevCode = fs.readFileSync(reactDevPath, 'utf8');
23
+
24
+ const reactDOMDevPath = path.join(
25
+ srcPath,
26
+ 'tests',
27
+ 'lib',
28
+ 'react-dom-v18.dev.js',
29
+ );
30
+ const reactDOMDevCode = fs.readFileSync(reactDOMDevPath, 'utf8');
31
+
32
+ // Create a simple test page with React and a custom component
33
+ await page.setContent(`
34
+ <html>
35
+ <body>
36
+ <div id="root"></div>
37
+ <script>${reactDevCode}</script>
38
+ <script>${reactDOMDevCode}</script>
39
+ <script>${bundleCode}</script>
40
+ <script>
41
+ function TestContainer() {
42
+ return React.createElement(
43
+ 'div',
44
+ null,
45
+ React.createElement(TestComponent),
46
+ React.createElement(TestComponent)
47
+ );
48
+ }
49
+
50
+ function TestComponent() {
51
+ return React.createElement('div', null, 'Test Component');
52
+ }
53
+
54
+ ReactDOM.render(
55
+ React.createElement(TestContainer),
56
+ document.getElementById('root')
57
+ );
58
+ </script>
59
+ </body>
60
+ </html>
61
+ `);
62
+
63
+ // Now the global `MemLens` should be available in the page
64
+ const componentIdentified = await page.evaluate(() => {
65
+ const createReactMemoryScan = (window as AnyValue).MemLens
66
+ .createReactMemoryScan;
67
+ const instance = createReactMemoryScan();
68
+ const analysisResult = instance.scan();
69
+ const componentCountCorrect =
70
+ analysisResult.componentToFiberNodeCount.get('TestComponent') === 2;
71
+ const containerCountCorrect =
72
+ analysisResult.componentToFiberNodeCount.get('TestContainer') === 1;
73
+ return componentCountCorrect && containerCountCorrect;
74
+ });
75
+
76
+ expect(componentIdentified).toBe(true);
77
+ });
@@ -0,0 +1,79 @@
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
+ import {test, expect} from '@playwright/test';
13
+ import path from 'path';
14
+ import fs from 'fs';
15
+ import {srcPath} from '../utils/test-utils';
16
+ import {libBundleFilePath} from '../utils/test-utils';
17
+
18
+ test('scan should identify react components (React 18 Prod)', async ({
19
+ page,
20
+ }) => {
21
+ const bundleCode = fs.readFileSync(libBundleFilePath, 'utf8');
22
+
23
+ const reactDevPath = path.join(srcPath, 'tests', 'lib', 'react-v18.prod.js');
24
+ const reactDevCode = fs.readFileSync(reactDevPath, 'utf8');
25
+
26
+ const reactDOMDevPath = path.join(
27
+ srcPath,
28
+ 'tests',
29
+ 'lib',
30
+ 'react-dom-v18.prod.js',
31
+ );
32
+ const reactDOMDevCode = fs.readFileSync(reactDOMDevPath, 'utf8');
33
+
34
+ // Create a simple test page with React and a custom component
35
+ await page.setContent(`
36
+ <html>
37
+ <body>
38
+ <div id="root"></div>
39
+ <script>${reactDevCode}</script>
40
+ <script>${reactDOMDevCode}</script>
41
+ <script>${bundleCode}</script>
42
+ <script>
43
+ function TestContainer() {
44
+ return React.createElement(
45
+ 'div',
46
+ null,
47
+ React.createElement(TestComponent),
48
+ React.createElement(TestComponent)
49
+ );
50
+ }
51
+
52
+ function TestComponent() {
53
+ return React.createElement('div', null, 'Test Component');
54
+ }
55
+
56
+ ReactDOM.render(
57
+ React.createElement(TestContainer),
58
+ document.getElementById('root')
59
+ );
60
+ </script>
61
+ </body>
62
+ </html>
63
+ `);
64
+
65
+ // Now the global `MemLens` should be available in the page
66
+ const componentIdentified = await page.evaluate(() => {
67
+ const createReactMemoryScan = (window as AnyValue).MemLens
68
+ .createReactMemoryScan;
69
+ const instance = createReactMemoryScan();
70
+ const analysisResult = instance.scan();
71
+ const componentCountCorrect =
72
+ analysisResult.componentToFiberNodeCount.get('TestComponent') === 2;
73
+ const containerCountCorrect =
74
+ analysisResult.componentToFiberNodeCount.get('TestContainer') === 1;
75
+ return componentCountCorrect && containerCountCorrect;
76
+ });
77
+
78
+ expect(componentIdentified).toBe(true);
79
+ });