Skip to content

Commit 5c93510

Browse files
committed
feat(chrome-devtools): add inspector
1 parent 4c5757b commit 5c93510

File tree

10 files changed

+355
-2
lines changed

10 files changed

+355
-2
lines changed

.changeset/few-dolls-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@module-federation/devtools': patch
3+
---
4+
5+
feat(chrome-devtools): add inspector

packages/chrome-devtools/manifest.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"static/js/post-message.js",
1818
"static/js/post-message-start.js",
1919
"static/js/fast-refresh.js",
20+
"static/js/inspector-plugin.js",
2021
"static/js/snapshot-plugin.js"
2122
],
2223
"matches": ["<all_urls>"]
@@ -35,7 +36,11 @@
3536
},
3637
{
3738
"matches": ["<all_urls>"],
38-
"js": ["static/js/fast-refresh.js", "static/js/snapshot-plugin.js"],
39+
"js": [
40+
"static/js/fast-refresh.js",
41+
"static/js/snapshot-plugin.js",
42+
"static/js/inspector-plugin.js"
43+
],
3944
"world": "MAIN",
4045
"run_at": "document_start"
4146
}

packages/chrome-devtools/modern.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export default defineConfig({
99
},
1010
},
1111
output: {
12+
injectStyles: process.env.INSPECTOR ? true : false,
13+
cleanDistPath: process.env.INSPECTOR ? true : false,
1214
disableInlineRuntimeChunk: true,
1315
disableFilenameHash: true,
1416
disableMinimize: true,
@@ -20,6 +22,16 @@ export default defineConfig({
2022
},
2123
tools: {
2224
webpack: (config: Record<string, any>) => {
25+
if (process.env.INSPECTOR) {
26+
config.entry = {
27+
'inspector-plugin': './src/utils/chrome/inspector-plugin.ts',
28+
};
29+
config.externals = {
30+
react: '_mfReact',
31+
};
32+
return config;
33+
}
34+
2335
if (process.env.E2ETEST) {
2436
config.entry.worker = './src/worker/index.ts';
2537
}

packages/chrome-devtools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"storybook": "storybook dev -p 6006",
1313
"reset": "npx rimraf ./**/node_modules",
1414
"dev": "modern dev",
15-
"build": "modern build && node postpack.js",
15+
"build": " INSPECTOR=true modern build && modern build && node postpack.js",
1616
"build:debug": "DEBUG=true modern build && node postpack.js",
1717
"build:lib": "rm -rf dist && modern-module build -c modern.lib.config.ts",
1818
"release": "npm publish --tag canary",

packages/chrome-devtools/src/component/Form/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ interface FormProps {
4444
setFormStatus: React.Dispatch<SetStateAction<FormItemStatus[]>>;
4545
validateForm: any;
4646
enableHMR: string;
47+
enableInspector: string;
4748
onHMRChange: (on: boolean) => void;
49+
onInspectorChange: (on: boolean) => void;
4850
}
4951
const FormComponent = (props: FormProps & RootComponentProps) => {
5052
const {
@@ -54,7 +56,9 @@ const FormComponent = (props: FormProps & RootComponentProps) => {
5456
setFormStatus,
5557
validateForm,
5658
enableHMR,
59+
enableInspector,
5760
onHMRChange,
61+
onInspectorChange,
5862
versionList,
5963
setVersionList,
6064
getVersion,
@@ -185,6 +189,10 @@ const FormComponent = (props: FormProps & RootComponentProps) => {
185189
onHMRChange(on);
186190
};
187191

192+
const inspectorChange = (on: boolean) => {
193+
onInspectorChange(on);
194+
};
195+
188196
const onKeyChange = async (key: string, index: number) => {
189197
const version = await getVersion?.(key);
190198
if (version) {
@@ -229,6 +237,13 @@ const FormComponent = (props: FormProps & RootComponentProps) => {
229237
onChange={hmrChange}
230238
className={styles.switch}
231239
/>
240+
<Switch
241+
checked={enableInspector === 'enable'}
242+
checkedText={'Enable Inspector'}
243+
uncheckedText={'Disable Inspector'}
244+
onChange={inspectorChange}
245+
className={styles.switch}
246+
/>
232247
</div>
233248
</div>
234249

packages/chrome-devtools/src/component/Layout/index.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {
3333
__ENABLE_FAST_REFRESH__,
3434
BROWSER_ENV_KEY,
3535
__FEDERATION_DEVTOOLS__,
36+
ENABLEINSPECTOR,
37+
__ENABLE_INSPECTOR__,
3638
} from '../../template/constant';
3739
interface FormItemType {
3840
key: string;
@@ -66,6 +68,7 @@ const Layout = (
6668
const [snapshot, setSnapshot] = useState(moduleInfo);
6769
const [form] = Form.useForm();
6870
const [enableHMR, setEnalbeHMR] = useState('disable');
71+
const [enableInspector, setEnalbeInspector] = useState('disable');
6972

7073
const { run } = useDebounceFn(
7174
async (formData) => {
@@ -159,6 +162,12 @@ const Layout = (
159162
onHMRChange(enable);
160163
}
161164
});
165+
chrome.storage.sync.get([ENABLEINSPECTOR]).then((data) => {
166+
const enable = data[ENABLEINSPECTOR];
167+
if (typeof enable === 'boolean') {
168+
onHMRChange(enable);
169+
}
170+
});
162171
}, []);
163172

164173
useEffect(() => {
@@ -200,6 +209,19 @@ const Layout = (
200209
injectScript(reloadPage, false);
201210
};
202211

212+
const onInspectorChange = (on: boolean) => {
213+
setEnalbeInspector(on ? 'enable' : 'disable');
214+
chrome.storage.sync.set({
215+
[ENABLEINSPECTOR]: on,
216+
});
217+
if (on) {
218+
mergeStorage(__FEDERATION_DEVTOOLS__, __ENABLE_INSPECTOR__, on);
219+
} else {
220+
removeStorageKey(__FEDERATION_DEVTOOLS__, __ENABLE_INSPECTOR__);
221+
}
222+
injectScript(reloadPage, false);
223+
};
224+
203225
return (
204226
<>
205227
<Form
@@ -215,6 +237,8 @@ const Layout = (
215237
validateForm={() => validateForm(form)}
216238
enableHMR={enableHMR}
217239
onHMRChange={onHMRChange}
240+
enableInspector={enableInspector}
241+
onInspectorChange={onInspectorChange}
218242
versionList={versionList}
219243
setVersionList={setVersionList}
220244
getVersion={getVersion}

packages/chrome-devtools/src/template/constant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const FormID = 'FormID';
22

33
export const ENABLEHMR = 'enableHMR';
4+
export const ENABLEINSPECTOR = 'enableInspector';
45

56
export const proxyFormField = 'proxyFormField';
67

@@ -51,6 +52,7 @@ export const statusInfo: Record<
5152
};
5253

5354
export const __ENABLE_FAST_REFRESH__ = 'enableFastRefresh';
55+
export const __ENABLE_INSPECTOR__ = 'enableInspector';
5456

5557
export const BROWSER_ENV_KEY = 'MF_ENV';
5658

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
.component-inspector {
2+
position: relative;
3+
display: inline-block;
4+
}
5+
6+
.inspector-info {
7+
position: fixed;
8+
z-index: 1000;
9+
background: #1a1a1a;
10+
padding: 4px 8px;
11+
border-radius: 4px;
12+
font-size: 12px;
13+
color: #fff;
14+
pointer-events: none;
15+
display: flex;
16+
align-items: center;
17+
gap: 8px;
18+
}
19+
20+
.inspector-overlay {
21+
position: fixed;
22+
border-radius: 4px;
23+
pointer-events: none;
24+
z-index: 999;
25+
background:
26+
linear-gradient(90deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 0,
27+
linear-gradient(90deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 100%,
28+
linear-gradient(0deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 0,
29+
linear-gradient(0deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 100% 0;
30+
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
31+
background-size:
32+
200% 3px,
33+
200% 3px,
34+
3px 200%,
35+
3px 200%; /* 将边框从 2px 加粗到 3px */
36+
animation: borderRotate 4s linear infinite;
37+
}
38+
39+
@keyframes borderRotate {
40+
0% {
41+
background-position:
42+
0% 0,
43+
0% 100%,
44+
0 0%,
45+
100% 0%;
46+
}
47+
100% {
48+
background-position:
49+
200% 0,
50+
-200% 100%,
51+
0 -200%,
52+
100% 200%;
53+
}
54+
}
55+
.mf-tag {
56+
color: #646cff;
57+
font-weight: 500;
58+
position: relative;
59+
padding-left: 16px;
60+
}
61+
62+
.mf-tag::before {
63+
content: '';
64+
position: absolute;
65+
left: 0;
66+
top: 50%;
67+
transform: translateY(-50%);
68+
width: 12px;
69+
height: 12px;
70+
background-image: url('https://module-federation.io/svg.svg');
71+
background-repeat: no-repeat;
72+
background-position: center;
73+
background-size: contain;
74+
}
75+
76+
.divider {
77+
color: #4a4a4a;
78+
}
79+
80+
.gradient-text {
81+
background: linear-gradient(90deg, #60a5fa, #ec4899);
82+
-webkit-background-clip: text;
83+
background-clip: text;
84+
-webkit-text-fill-color: transparent;
85+
font-weight: 500;
86+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type React from 'react';
2+
import './ComponentInspector.css';
3+
4+
interface InspectorInfo {
5+
top: number;
6+
left: number;
7+
width: number;
8+
height: number;
9+
}
10+
11+
export const wrapComponent = ({
12+
react,
13+
CustomComponent,
14+
componentName,
15+
mfName,
16+
}: {
17+
react: typeof React;
18+
CustomComponent: React.ComponentType;
19+
componentName: string;
20+
mfName: string;
21+
}) => {
22+
const ComponentInspector: React.FC<{
23+
children: React.ReactNode;
24+
componentName: string;
25+
mfName: string;
26+
}> = ({ children, componentName, mfName }) => {
27+
const [inspectorInfo, setInspectorInfo] =
28+
react.useState<InspectorInfo | null>(null);
29+
30+
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
31+
const rect = e.currentTarget.getBoundingClientRect();
32+
setInspectorInfo({
33+
top: rect.top,
34+
left: rect.left,
35+
width: rect.width,
36+
height: rect.height,
37+
});
38+
};
39+
40+
const handleMouseLeave = () => {
41+
setInspectorInfo(null);
42+
};
43+
44+
return (
45+
<div
46+
className="component-inspector"
47+
onMouseEnter={handleMouseEnter}
48+
onMouseLeave={handleMouseLeave}
49+
>
50+
{children}
51+
{inspectorInfo && (
52+
<>
53+
<div
54+
className="inspector-info"
55+
style={{
56+
top: `${inspectorInfo.top - 30}px`,
57+
left: `${inspectorInfo.left}px`,
58+
width: `${inspectorInfo.width}px`,
59+
}}
60+
>
61+
<span className="mf-tag">{mfName}</span>
62+
<span className="divider">|</span>
63+
<span className="gradient-text">{componentName}</span>
64+
</div>
65+
<div
66+
className="inspector-overlay"
67+
style={{
68+
top: `${inspectorInfo.top}px`,
69+
left: `${inspectorInfo.left}px`,
70+
width: `${inspectorInfo.width}px`,
71+
height: `${inspectorInfo.height}px`,
72+
}}
73+
/>
74+
</>
75+
)}
76+
</div>
77+
);
78+
};
79+
80+
return (
81+
<ComponentInspector componentName={componentName} mfName={mfName}>
82+
<CustomComponent />
83+
</ComponentInspector>
84+
);
85+
};

0 commit comments

Comments
 (0)