@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.
- package/README.md +63 -0
- package/dist/index.js +112 -0
- package/explainer.md +54 -0
- package/package.json +34 -0
- package/playwright.config.ts +21 -0
- package/src/config/config.ts +32 -0
- package/src/core/dom-observer.ts +189 -0
- package/src/core/event-listener-tracker.ts +171 -0
- package/src/core/react-fiber-analysis.ts +123 -0
- package/src/core/react-memory-scan.ts +366 -0
- package/src/core/types.ts +180 -0
- package/src/core/valid-component-name.ts +17 -0
- package/src/extensions/basic-extension.ts +41 -0
- package/src/extensions/dom-visualization-extension.ts +42 -0
- package/src/index.ts +16 -0
- package/src/memlens.lib.js.flow +75 -0
- package/src/memlens.lib.ts +22 -0
- package/src/memlens.run.ts +21 -0
- package/src/tests/bundle/lib.bundle.test.ts +31 -0
- package/src/tests/bundle/run.bundle.start.head.test.ts +48 -0
- package/src/tests/bundle/run.bundle.start.test.ts +48 -0
- package/src/tests/bundle/run.bundle.test.ts +51 -0
- package/src/tests/fiber/dev.fiber.complex.dev.test.ts +92 -0
- package/src/tests/fiber/dev.fiber.complex.list.dev.test.ts +118 -0
- package/src/tests/fiber/dev.fiber.complex.prod.test.ts +92 -0
- package/src/tests/fiber/dev.fiber.name.dev.test.ts +77 -0
- package/src/tests/fiber/dev.fiber.name.prod.test.ts +79 -0
- package/src/tests/lib/babel.prod.js +4 -0
- package/src/tests/lib/react-dom-v18.dev.js +29926 -0
- package/src/tests/lib/react-dom-v18.prod.js +269 -0
- package/src/tests/lib/react-v18.dev.js +3345 -0
- package/src/tests/lib/react-v18.prod.js +33 -0
- package/src/tests/manual/playwright-open-manual.js +40 -0
- package/src/tests/manual/todo-list/todo-with-run.bundle.html +80 -0
- package/src/tests/utils/test-utils.ts +28 -0
- package/src/utils/intersection-observer.ts +65 -0
- package/src/utils/react-fiber-utils.ts +212 -0
- package/src/utils/utils.ts +201 -0
- package/src/utils/weak-ref-utils.ts +308 -0
- package/src/visual/components/component-stack-panel.ts +85 -0
- package/src/visual/components/control-widget.ts +96 -0
- package/src/visual/components/overlay-rectangle.ts +167 -0
- package/src/visual/components/status-text.ts +53 -0
- package/src/visual/components/toggle-button.ts +57 -0
- package/src/visual/components/visual-overlay.ts +19 -0
- package/src/visual/dom-element-visualizer-interactive.ts +358 -0
- package/src/visual/dom-element-visualizer.ts +130 -0
- package/src/visual/visual-utils.ts +89 -0
- package/tsconfig.json +18 -0
- 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
|
+
});
|