@mf-toolkit/shared-inspector 0.3.2 → 0.4.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.
- package/README.md +320 -156
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +98 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/colorize-report.d.ts +6 -0
- package/dist/cli/colorize-report.d.ts.map +1 -0
- package/dist/cli/colorize-report.js +71 -0
- package/dist/cli/colorize-report.js.map +1 -0
- package/dist/cli/colors.d.ts +14 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/colors.js +26 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +30 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +62 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +8 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +41 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/run-federation.d.ts +3 -0
- package/dist/cli/run-federation.d.ts.map +1 -0
- package/dist/cli/run-federation.js +41 -0
- package/dist/cli/run-federation.js.map +1 -0
- package/dist/cli/run-project.d.ts +5 -0
- package/dist/cli/run-project.d.ts.map +1 -0
- package/dist/cli/run-project.js +57 -0
- package/dist/cli/run-project.js.map +1 -0
- package/dist/cli/spinner.d.ts +10 -0
- package/dist/cli/spinner.d.ts.map +1 -0
- package/dist/cli/spinner.js +45 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/types.d.ts +17 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/reporter/diagnostics.d.ts +23 -0
- package/dist/reporter/diagnostics.d.ts.map +1 -0
- package/dist/reporter/diagnostics.js +138 -0
- package/dist/reporter/diagnostics.js.map +1 -0
- package/dist/reporter/format-federation-report.d.ts +11 -5
- package/dist/reporter/format-federation-report.d.ts.map +1 -1
- package/dist/reporter/format-federation-report.js +78 -37
- package/dist/reporter/format-federation-report.js.map +1 -1
- package/dist/reporter/format-report.d.ts +14 -7
- package/dist/reporter/format-report.d.ts.map +1 -1
- package/dist/reporter/format-report.js +67 -38
- package/dist/reporter/format-report.js.map +1 -1
- package/dist/reporter/scoring.d.ts +28 -0
- package/dist/reporter/scoring.d.ts.map +1 -0
- package/dist/reporter/scoring.js +74 -0
- package/dist/reporter/scoring.js.map +1 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -4,18 +4,95 @@
|
|
|
4
4
|
[](https://github.com/zvitaly7/mf-toolkit/blob/main/LICENSE)
|
|
5
5
|
[](https://nodejs.org)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Stop debugging Module Federation in production.**
|
|
8
|
+
|
|
9
|
+
`shared` config breaks in silence — wrong versions ship, duplicate React copies can end up in the bundle, singleton negotiation fails, and teams get paged for "Invalid hook call" on Friday night. `shared-inspector` catches these mistakes at build time. Every finding comes with a risk score and a ready-to-paste fix.
|
|
8
10
|
|
|
9
11
|
## The problem
|
|
10
12
|
|
|
11
13
|
Module Federation teams manually manage `shared` config and make three kinds of mistakes:
|
|
12
14
|
|
|
13
15
|
- **Over-sharing** — packages listed in `shared` that the microfrontend never imports. Creates artificial version coupling between independent teams.
|
|
14
|
-
- **Under-sharing** — packages used by both host and remote but missing from `shared`. Each microfrontend
|
|
16
|
+
- **Under-sharing** — packages used by both host and remote but missing from `shared`. Each microfrontend may bundle its own copy (e.g. multiple React instances, each ~130 KB).
|
|
15
17
|
- **Version mismatch** — `requiredVersion` doesn't match the installed version. Module Federation silently falls back to a local bundle. For packages with global state (React, styled-components) this causes "Invalid hook call" in production.
|
|
16
18
|
|
|
17
19
|
Existing tools (webpack-bundle-analyzer, source-map-explorer) show *what ended up in the bundle*, not *why shared config is suboptimal*. Different questions.
|
|
18
20
|
|
|
21
|
+
## Why not bundle analyzer?
|
|
22
|
+
|
|
23
|
+
Bundle analyzers (webpack-bundle-analyzer, source-map-explorer, stats.json inspection) answer a different question: *what is in the output?* They are useful for auditing final bundle size, but they don't model Module Federation's shared dependency negotiation.
|
|
24
|
+
|
|
25
|
+
| Question | Bundle analyzer | shared-inspector |
|
|
26
|
+
|----------|----------------|-----------------|
|
|
27
|
+
| Which packages are large? | ✅ | — |
|
|
28
|
+
| Is React duplicated across MFs? | Visible after the fact | ✅ Detected before build ships |
|
|
29
|
+
| Is `requiredVersion` out of sync with the installed version? | ✗ | ✅ |
|
|
30
|
+
| Is a package marked `singleton` in one MF but not another? | ✗ | ✅ |
|
|
31
|
+
| Which packages are declared `shared` but never imported? | ✗ | ✅ |
|
|
32
|
+
| Which used packages are missing from `shared` entirely? | ✗ | ✅ |
|
|
33
|
+
| Cross-MF version conflicts across teams? | ✗ | ✅ via federation manifests |
|
|
34
|
+
|
|
35
|
+
In short: bundle analyzers are useful for post-build inspection. `shared-inspector` is focused on the `shared` config itself — catching misconfiguration at build time and explaining what the runtime consequences would be.
|
|
36
|
+
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
A `shell` host app (React 18) and a `checkout` remote have been developed by separate teams. Their `shared` configs have drifted:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// shell — webpack.config.js
|
|
43
|
+
shared: {
|
|
44
|
+
react: { singleton: true, requiredVersion: '^18.2.0' },
|
|
45
|
+
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
|
|
46
|
+
zustand: { singleton: true },
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// checkout — webpack.config.js
|
|
50
|
+
shared: {
|
|
51
|
+
react: { singleton: true, requiredVersion: '^17.0.2' }, // ← stale version
|
|
52
|
+
'react-dom': { singleton: true, requiredVersion: '^17.0.2' },
|
|
53
|
+
lodash: {}, // ← never imported
|
|
54
|
+
// zustand: missing — checkout imports it, but it's not in shared
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Running `npx @mf-toolkit/shared-inspector` in the `checkout` project:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
[MfSharedInspector] checkout (depth: local-graph, 34 files scanned)
|
|
62
|
+
────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
⚠ Version Mismatch — react
|
|
65
|
+
configured: ^17.0.2 | installed: 17.0.2
|
|
66
|
+
→ Risk: Invalid hook call, broken context across MFs
|
|
67
|
+
💡 Fix:
|
|
68
|
+
shared: {
|
|
69
|
+
react: { singleton: true, requiredVersion: "^18.2.0" }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
→ Not Shared — zustand (8 imports in 5 files)
|
|
73
|
+
→ Risk: Each MF may get its own store instance — state changes may not propagate across MFs
|
|
74
|
+
💡 Fix:
|
|
75
|
+
shared: {
|
|
76
|
+
zustand: { singleton: true }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
✗ Unused Shared — lodash
|
|
80
|
+
0 imports, shared without singleton
|
|
81
|
+
→ Wastes bundle negotiation overhead with no benefit
|
|
82
|
+
💡 Fix: Remove "lodash" from shared config
|
|
83
|
+
|
|
84
|
+
────────────────────────────────────────────────────────────
|
|
85
|
+
Score: 69/100 🟠 RISKY
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**After manually updating the config based on the suggestions above** — `react` version aligned, `zustand` added to `shared`, `lodash` removed:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Score: 100/100 ✅ HEALTHY
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The cross-team federation report also clears: `shell` and `checkout` now negotiate a single React instance and a single Zustand store at runtime.
|
|
95
|
+
|
|
19
96
|
## Installation
|
|
20
97
|
|
|
21
98
|
```bash
|
|
@@ -24,58 +101,85 @@ npm install --save-dev @mf-toolkit/shared-inspector
|
|
|
24
101
|
|
|
25
102
|
## Quick start
|
|
26
103
|
|
|
27
|
-
|
|
104
|
+
Run against any MF project — no config file needed:
|
|
28
105
|
|
|
29
|
-
```
|
|
30
|
-
|
|
106
|
+
```bash
|
|
107
|
+
npx @mf-toolkit/shared-inspector
|
|
108
|
+
```
|
|
31
109
|
|
|
32
|
-
|
|
33
|
-
const manifest = await buildProjectManifest({
|
|
34
|
-
name: 'checkout',
|
|
35
|
-
sourceDirs: ['./src'],
|
|
36
|
-
sharedConfig: {
|
|
37
|
-
react: { singleton: true, requiredVersion: '^19.0.0' },
|
|
38
|
-
'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
|
|
39
|
-
lodash: {},
|
|
40
|
-
},
|
|
41
|
-
// depth: 'local-graph' ← default, follows barrel re-exports
|
|
42
|
-
// tsconfigPath: './tsconfig.json' ← optional, resolves @alias/* imports
|
|
43
|
-
// workspacePackages: ['@my-org/*'] ← optional, excludes local monorepo packages
|
|
44
|
-
});
|
|
110
|
+
The tool scans `./src`, reads installed versions from `package.json`, and prints a diagnostic report. Each finding includes what's wrong, what breaks at runtime, and a ready-to-paste fix:
|
|
45
111
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
alwaysShared: ['react', 'react-dom'],
|
|
49
|
-
});
|
|
112
|
+
```
|
|
113
|
+
mf-inspector v0.4.0
|
|
50
114
|
|
|
51
|
-
|
|
52
|
-
// [{ package: 'lodash', singleton: false }]
|
|
115
|
+
✓ Scanned 47 files
|
|
53
116
|
|
|
54
|
-
|
|
55
|
-
|
|
117
|
+
[MfSharedInspector] checkout (depth: local-graph, 47 files scanned)
|
|
118
|
+
────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
⚠ Version Mismatch — react
|
|
121
|
+
configured: ^18.0.0 | installed: 17.0.2
|
|
122
|
+
→ Risk: Invalid hook call, broken context across MFs
|
|
123
|
+
💡 Fix:
|
|
124
|
+
shared: {
|
|
125
|
+
react: { singleton: true, requiredVersion: "^18.0.0" }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
✗ Unused Shared — lodash
|
|
129
|
+
0 imports, shared without singleton
|
|
130
|
+
→ Wastes bundle negotiation overhead with no benefit
|
|
131
|
+
💡 Fix: Remove "lodash" from shared config
|
|
132
|
+
|
|
133
|
+
→ Not Shared — mobx (12 imports in 8 files via re-export in src/shared/index.ts)
|
|
134
|
+
→ Risk: Each MF may get its own MobX instance — observables and reactions can fail to sync between MFs
|
|
135
|
+
💡 Fix:
|
|
136
|
+
shared: {
|
|
137
|
+
mobx: { singleton: true }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
────────────────────────────────────────────────────────────
|
|
141
|
+
Score: 62/100 🟠 RISKY
|
|
142
|
+
|
|
143
|
+
Issues:
|
|
144
|
+
🔴 1 high — version mismatch
|
|
145
|
+
🟠 1 medium — singleton gaps, duplicate libs
|
|
146
|
+
🟡 1 low — over-sharing
|
|
147
|
+
|
|
148
|
+
Total: 12 shared, 10 used, 1 unused, 1 candidates, 1 mismatch, 0 eager risks
|
|
149
|
+
```
|
|
56
150
|
|
|
57
|
-
|
|
58
|
-
// [{ package: 'react', configured: '^19.0.0', installed: '18.3.1' }]
|
|
151
|
+
Colors are auto-applied in TTY terminals and disabled in CI / piped output (`NO_COLOR` / `TERM=dumb` respected).
|
|
59
152
|
|
|
60
|
-
|
|
61
|
-
// [{ package: 'react-dom' }] ← eager: true without singleton: true
|
|
62
|
-
```
|
|
153
|
+
### Risk scoring
|
|
63
154
|
|
|
64
|
-
|
|
155
|
+
Every report ends with a score out of 100:
|
|
65
156
|
|
|
66
|
-
|
|
67
|
-
|
|
157
|
+
| Severity | Penalty | Covers |
|
|
158
|
+
|----------|---------|--------|
|
|
159
|
+
| 🔴 HIGH | −20 each | Version mismatches, cross-MF version conflicts |
|
|
160
|
+
| 🟠 MEDIUM | −8 each | Singleton risks, eager risks, duplicate libs, host gaps |
|
|
161
|
+
| 🟡 LOW | −3 each | Unused shared packages, ghost shares |
|
|
68
162
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
163
|
+
| Score | Label |
|
|
164
|
+
|-------|-------|
|
|
165
|
+
| 90–100 | ✅ HEALTHY |
|
|
166
|
+
| 70–89 | 🟡 GOOD |
|
|
167
|
+
| 40–69 | 🟠 RISKY |
|
|
168
|
+
| 0–39 | 🔴 CRITICAL |
|
|
75
169
|
|
|
76
|
-
|
|
170
|
+
## CI mode
|
|
77
171
|
|
|
78
|
-
|
|
172
|
+
Integrate into build pipelines to fail on findings, gate on score, or emit manifests for later federation analysis.
|
|
173
|
+
|
|
174
|
+
### Failing the build
|
|
175
|
+
|
|
176
|
+
Use `--fail-on` to exit with code 1 when specific findings are detected:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx @mf-toolkit/shared-inspector --source ./src --shared react,react-dom --fail-on mismatch
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
With the webpack plugin:
|
|
79
183
|
|
|
80
184
|
```typescript
|
|
81
185
|
import { MfSharedInspectorPlugin } from '@mf-toolkit/shared-inspector/webpack';
|
|
@@ -90,144 +194,206 @@ module.exports = {
|
|
|
90
194
|
// sharedConfig not needed — auto-extracted from ModuleFederationPlugin above
|
|
91
195
|
new MfSharedInspectorPlugin({
|
|
92
196
|
sourceDirs: ['./src'],
|
|
197
|
+
failOn: 'mismatch', // 'mismatch' | 'unused' | 'any'
|
|
93
198
|
warn: true,
|
|
94
|
-
writeManifest: true, // writes project-manifest.json for CI aggregation
|
|
95
199
|
}),
|
|
96
200
|
],
|
|
97
201
|
};
|
|
98
202
|
```
|
|
99
203
|
|
|
100
|
-
|
|
204
|
+
### Gating on score
|
|
205
|
+
|
|
206
|
+
Use `scoreProjectReport` programmatically to set custom thresholds:
|
|
101
207
|
|
|
102
208
|
```typescript
|
|
103
|
-
|
|
104
|
-
sourceDirs: ['./src'],
|
|
105
|
-
sharedConfig: { react: { singleton: true, requiredVersion: '^18.0.0' } },
|
|
106
|
-
})
|
|
107
|
-
```
|
|
209
|
+
import { analyzeProject, scoreProjectReport } from '@mf-toolkit/shared-inspector';
|
|
108
210
|
|
|
109
|
-
|
|
211
|
+
const report = analyzeProject(manifest);
|
|
212
|
+
const { score, label, high, medium, low } = scoreProjectReport(report);
|
|
110
213
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
214
|
+
if (score < 70) {
|
|
215
|
+
console.error(`Shared config score: ${score}/100 (${label})`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
115
219
|
|
|
116
|
-
|
|
220
|
+
### Writing manifests for federation analysis
|
|
117
221
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
222
|
+
Each MF can emit a `project-manifest.json` as a build artifact to enable cross-team analysis in a later step:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npx @mf-toolkit/shared-inspector --shared ./shared-config.json --write-manifest
|
|
122
226
|
```
|
|
123
227
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
228
|
+
With the webpack plugin:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
new MfSharedInspectorPlugin({
|
|
232
|
+
sourceDirs: ['./src'],
|
|
233
|
+
writeManifest: true, // writes project-manifest.json for CI aggregation
|
|
234
|
+
warn: true,
|
|
235
|
+
})
|
|
127
236
|
```
|
|
128
237
|
|
|
129
|
-
|
|
130
|
-
- **`depth: 'local-graph'`** follows `./shared` → `shared/index.ts` → finds `mobx` and `mobx-react` via re-export.
|
|
238
|
+
Upload the manifest as a CI artifact:
|
|
131
239
|
|
|
132
|
-
|
|
240
|
+
```yaml
|
|
241
|
+
# .github/workflows/build.yml
|
|
242
|
+
jobs:
|
|
243
|
+
build-checkout:
|
|
244
|
+
steps:
|
|
245
|
+
- run: npm run build # MfSharedInspectorPlugin writes project-manifest.json
|
|
246
|
+
- uses: actions/upload-artifact@v4
|
|
247
|
+
with: { name: manifest-checkout, path: project-manifest.json }
|
|
248
|
+
```
|
|
133
249
|
|
|
134
|
-
|
|
250
|
+
## Federation mode
|
|
135
251
|
|
|
136
|
-
|
|
137
|
-
// tsconfig.json
|
|
138
|
-
{ "compilerOptions": { "baseUrl": ".", "paths": { "@components/*": ["src/components/*"] } } }
|
|
252
|
+
Once each MF has emitted its manifest, aggregate them to detect cross-team conflicts: version mismatches, singleton inconsistencies, and shared-config gaps across host and remotes.
|
|
139
253
|
|
|
140
|
-
|
|
141
|
-
|
|
254
|
+
### CLI
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npx @mf-toolkit/shared-inspector federation checkout.json catalog.json cart.json
|
|
142
258
|
```
|
|
143
259
|
|
|
260
|
+
### Programmatic
|
|
261
|
+
|
|
144
262
|
```typescript
|
|
145
|
-
|
|
146
|
-
sourceDirs: ['./src'],
|
|
147
|
-
tsconfigPath: './tsconfig.json', // enables alias resolution
|
|
148
|
-
});
|
|
149
|
-
```
|
|
263
|
+
import { analyzeFederation, formatFederationReport, scoreFederationReport } from '@mf-toolkit/shared-inspector';
|
|
150
264
|
|
|
151
|
-
|
|
265
|
+
const report = analyzeFederation([checkoutManifest, catalogManifest, cartManifest]);
|
|
266
|
+
const { score, label } = scoreFederationReport(report);
|
|
267
|
+
|
|
268
|
+
console.log(formatFederationReport(report));
|
|
269
|
+
// ⚠ Version Conflict — react
|
|
270
|
+
// checkout: ^17.0.0
|
|
271
|
+
// catalog: ^18.0.0
|
|
272
|
+
// → Risk: MF singleton negotiation may silently load the wrong version → Invalid hook call
|
|
273
|
+
// 💡 Fix: shared: { react: { singleton: true, requiredVersion: "^18.0.0" } }
|
|
274
|
+
//
|
|
275
|
+
// Score: 60/100 🟠 RISKY
|
|
276
|
+
```
|
|
152
277
|
|
|
153
|
-
##
|
|
278
|
+
## Programmatic API
|
|
154
279
|
|
|
155
|
-
|
|
280
|
+
### Two-phase API
|
|
156
281
|
|
|
157
282
|
```typescript
|
|
158
|
-
|
|
283
|
+
import { buildProjectManifest, analyzeProject, formatReport } from '@mf-toolkit/shared-inspector';
|
|
284
|
+
|
|
285
|
+
// Phase 1: collect facts
|
|
286
|
+
const manifest = await buildProjectManifest({
|
|
287
|
+
name: 'checkout',
|
|
159
288
|
sourceDirs: ['./src'],
|
|
160
|
-
|
|
289
|
+
sharedConfig: {
|
|
290
|
+
react: { singleton: true, requiredVersion: '^18.0.0' },
|
|
291
|
+
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
|
|
292
|
+
lodash: {},
|
|
293
|
+
},
|
|
294
|
+
// depth: 'local-graph' ← default, follows barrel re-exports
|
|
295
|
+
// tsconfigPath: './tsconfig.json' ← optional, resolves @alias/* imports
|
|
296
|
+
// workspacePackages: ['@my-org/*'] ← optional, excludes local monorepo packages
|
|
161
297
|
});
|
|
162
|
-
```
|
|
163
298
|
|
|
164
|
-
|
|
299
|
+
// Phase 2: analyse facts
|
|
300
|
+
const report = analyzeProject(manifest);
|
|
165
301
|
|
|
166
|
-
|
|
167
|
-
[
|
|
302
|
+
console.log(report.mismatched);
|
|
303
|
+
// [{ package: 'react', configured: '^18.0.0', installed: '17.0.2' }]
|
|
168
304
|
|
|
169
|
-
|
|
170
|
-
|
|
305
|
+
console.log(report.candidates);
|
|
306
|
+
// [{ package: 'mobx', importCount: 12, via: 'reexport', files: ['src/shared/index.ts'] }]
|
|
171
307
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
✗ @tanstack/react-query — 0 imports, shared as singleton
|
|
308
|
+
console.log(report.unused);
|
|
309
|
+
// [{ package: 'lodash', singleton: false }]
|
|
175
310
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
311
|
+
// Human-readable output with risk descriptions and fix snippets
|
|
312
|
+
console.log(formatReport(report, { name: manifest.project.name }));
|
|
313
|
+
```
|
|
179
314
|
|
|
180
|
-
|
|
181
|
-
⚠ react-router-dom — manages global state, singleton: true recommended
|
|
315
|
+
### Shortcut API
|
|
182
316
|
|
|
183
|
-
|
|
184
|
-
|
|
317
|
+
```typescript
|
|
318
|
+
import { inspect } from '@mf-toolkit/shared-inspector';
|
|
185
319
|
|
|
186
|
-
|
|
320
|
+
const report = await inspect({
|
|
321
|
+
name: 'checkout',
|
|
322
|
+
sourceDirs: ['./src'],
|
|
323
|
+
sharedConfig: { /* ... */ },
|
|
324
|
+
});
|
|
187
325
|
```
|
|
188
326
|
|
|
189
|
-
##
|
|
327
|
+
## Analysis depth
|
|
328
|
+
|
|
329
|
+
| Depth | What it finds | Speed |
|
|
330
|
+
|-------|--------------|-------|
|
|
331
|
+
| `'direct'` | Explicit `import` / `require` statements | Fast (ms) |
|
|
332
|
+
| `'local-graph'` *(default)* | + packages reachable via barrel re-exports and local wrappers | Slower (seconds) |
|
|
333
|
+
|
|
334
|
+
The difference matters when your project uses barrel files:
|
|
190
335
|
|
|
191
|
-
|
|
336
|
+
```ts
|
|
337
|
+
// src/shared/index.ts
|
|
338
|
+
export { observer } from 'mobx-react'; // re-export
|
|
339
|
+
export { makeAutoObservable } from 'mobx'; // re-export
|
|
192
340
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
build-checkout:
|
|
196
|
-
steps:
|
|
197
|
-
- run: npm run build # MfSharedInspectorPlugin writes project-manifest.json
|
|
198
|
-
- uses: actions/upload-artifact@v4
|
|
199
|
-
with:
|
|
200
|
-
name: manifest-checkout
|
|
201
|
-
path: project-manifest.json
|
|
341
|
+
// src/app.tsx
|
|
342
|
+
import { observer } from './shared'; // relative import — direct mode stops here
|
|
202
343
|
```
|
|
203
344
|
|
|
204
|
-
|
|
345
|
+
- **`depth: 'direct'`** sees `./shared` (relative) → skips. `mobx` not found.
|
|
346
|
+
- **`depth: 'local-graph'`** follows `./shared` → finds `mobx` and `mobx-react` via re-export.
|
|
347
|
+
|
|
348
|
+
## TypeScript path aliases
|
|
205
349
|
|
|
206
350
|
```typescript
|
|
207
|
-
|
|
351
|
+
// tsconfig.json
|
|
352
|
+
{ "compilerOptions": { "paths": { "@components/*": ["src/components/*"] } } }
|
|
208
353
|
|
|
209
|
-
|
|
210
|
-
|
|
354
|
+
await buildProjectManifest({
|
|
355
|
+
sourceDirs: ['./src'],
|
|
356
|
+
tsconfigPath: './tsconfig.json', // enables alias resolution
|
|
357
|
+
});
|
|
211
358
|
```
|
|
212
359
|
|
|
213
|
-
|
|
214
|
-
[MfSharedInspector] federation analysis (3 MFs)
|
|
360
|
+
Without `tsconfigPath`, `@components/Button` is treated as an external package and packages imported inside it are invisible in `local-graph` mode.
|
|
215
361
|
|
|
216
|
-
|
|
217
|
-
⚠ react — checkout: ^17.0.0, catalog: ^18.0.0
|
|
362
|
+
## Interactive wizard
|
|
218
363
|
|
|
219
|
-
|
|
220
|
-
⚠ mobx — singleton in [checkout], not singleton in [catalog, cart]
|
|
364
|
+
Not sure which flags to pass? Run the step-by-step wizard:
|
|
221
365
|
|
|
222
|
-
|
|
223
|
-
|
|
366
|
+
```
|
|
367
|
+
$ npx @mf-toolkit/shared-inspector --interactive
|
|
224
368
|
|
|
225
|
-
|
|
226
|
-
✗ lodash — shared only by cart, unused by all other MFs
|
|
369
|
+
[MfSharedInspector] Interactive setup
|
|
227
370
|
|
|
228
|
-
|
|
371
|
+
Source directories to scan (default: ./src):
|
|
372
|
+
Scan depth — direct or local-graph (default: local-graph):
|
|
373
|
+
Shared packages — comma-separated names or path to .json (empty to skip): react,react-dom,mobx
|
|
374
|
+
Path to tsconfig.json for alias resolution (empty to skip):
|
|
375
|
+
Workspace packages to exclude, comma-separated (empty to skip):
|
|
376
|
+
Fail build on findings — mismatch / unused / any / none (default: none): mismatch
|
|
377
|
+
Write project-manifest.json? (y/N): n
|
|
229
378
|
```
|
|
230
379
|
|
|
380
|
+
## CLI reference
|
|
381
|
+
|
|
382
|
+
| Flag | Default | Description |
|
|
383
|
+
|------|---------|-------------|
|
|
384
|
+
| `--source, -s <dirs>` | `./src` | Source dirs to scan, comma-separated |
|
|
385
|
+
| `--depth <depth>` | `local-graph` | Scan depth: `direct` \| `local-graph` |
|
|
386
|
+
| `--shared <packages\|file>` | — | Comma-separated package names or path to `.json` config |
|
|
387
|
+
| `--tsconfig <path>` | — | tsconfig.json for path alias resolution |
|
|
388
|
+
| `--workspace-packages <pkgs>` | — | Comma-separated workspace packages to exclude |
|
|
389
|
+
| `--name <name>` | auto from `package.json` | Project name |
|
|
390
|
+
| `--fail-on <rule>` | — | Exit 1 when findings match: `mismatch` \| `unused` \| `any` |
|
|
391
|
+
| `--write-manifest` | `false` | Write `project-manifest.json` to output dir |
|
|
392
|
+
| `--output-dir <dir>` | `.` | Output directory for manifest |
|
|
393
|
+
| `--interactive, -i` | — | Launch step-by-step wizard |
|
|
394
|
+
| `--version, -v` | — | Print version and exit |
|
|
395
|
+
| `--help, -h` | — | Show help |
|
|
396
|
+
|
|
231
397
|
## API reference
|
|
232
398
|
|
|
233
399
|
### `buildProjectManifest(options)`
|
|
@@ -253,6 +419,20 @@ console.log(formatFederationReport(report));
|
|
|
253
419
|
| `additionalCandidates` | `string[]` | `[]` | Extend built-in candidates list |
|
|
254
420
|
| `additionalSingletonRisks` | `string[]` | `[]` | Extend built-in singleton-risk list |
|
|
255
421
|
|
|
422
|
+
### `scoreProjectReport(report)` / `scoreFederationReport(report)`
|
|
423
|
+
|
|
424
|
+
Returns a `RiskScore`:
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
interface RiskScore {
|
|
428
|
+
score: number; // 0–100, higher is better
|
|
429
|
+
label: 'HEALTHY' | 'GOOD' | 'RISKY' | 'CRITICAL';
|
|
430
|
+
high: number; // count of high-severity findings
|
|
431
|
+
medium: number; // count of medium-severity findings
|
|
432
|
+
low: number; // count of low-severity findings
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
256
436
|
### `analyzeFederation(manifests, options?)`
|
|
257
437
|
|
|
258
438
|
Accepts N `ProjectManifest` objects (one per microfrontend) and returns a `FederationReport`.
|
|
@@ -261,20 +441,12 @@ Accepts N `ProjectManifest` objects (one per microfrontend) and returns a `Feder
|
|
|
261
441
|
|--------|------|---------|-------------|
|
|
262
442
|
| `alwaysShared` | `string[]` | `['react','react-dom']` | Exclude from ghost/gap detection |
|
|
263
443
|
|
|
264
|
-
### `formatFederationReport(report)`
|
|
265
|
-
|
|
266
|
-
Formats a `FederationReport` as a human-readable terminal string.
|
|
267
|
-
|
|
268
444
|
### `MfSharedInspectorPlugin` options
|
|
269
445
|
|
|
270
446
|
Extends all `buildProjectManifest` options (except `name`, auto-resolved from compiler) plus:
|
|
271
447
|
|
|
272
448
|
| Option | Type | Default | Description |
|
|
273
449
|
|--------|------|---------|-------------|
|
|
274
|
-
| `sourceDirs` | `string[]` | — | Directories to scan |
|
|
275
|
-
| `sharedConfig` | `Record<string, SharedDepConfig>` | auto-extracted | Override auto-extraction from `ModuleFederationPlugin` |
|
|
276
|
-
| `tsconfigPath` | `string` | `undefined` | tsconfig.json for path alias resolution |
|
|
277
|
-
| `workspacePackages` | `string[]` | `[]` | Local monorepo packages to exclude |
|
|
278
450
|
| `warn` | `boolean` | `true` | Print findings to console |
|
|
279
451
|
| `failOn` | `'mismatch' \| 'unused' \| 'any'` | `undefined` | Fail the build when findings match |
|
|
280
452
|
| `writeManifest` | `boolean` | `false` | Write `project-manifest.json` to `outputDir` |
|
|
@@ -285,37 +457,29 @@ Extends all `buildProjectManifest` options (except `name`, auto-resolved from co
|
|
|
285
457
|
|
|
286
458
|
### Per-project (`analyzeProject`)
|
|
287
459
|
|
|
288
|
-
| Category |
|
|
289
|
-
|
|
290
|
-
| `mismatched` |
|
|
291
|
-
| `
|
|
292
|
-
| `
|
|
293
|
-
| `
|
|
294
|
-
| `
|
|
295
|
-
|
|
296
|
-
*Within the visibility of the chosen depth.*
|
|
460
|
+
| Category | Severity | Description |
|
|
461
|
+
|----------|----------|-------------|
|
|
462
|
+
| `mismatched` | 🔴 HIGH | `requiredVersion` doesn't satisfy installed version |
|
|
463
|
+
| `singletonRisks` | 🟠 MEDIUM | Global-state packages shared without `singleton: true` |
|
|
464
|
+
| `eagerRisks` | 🟠 MEDIUM | `eager: true` without `singleton: true` |
|
|
465
|
+
| `candidates` | 🟠 MEDIUM | Used packages missing from `shared` (each MF bundles own copy) |
|
|
466
|
+
| `unused` | 🟡 LOW | In `shared` config but not observed in scanned sources |
|
|
297
467
|
|
|
298
468
|
### Cross-MF (`analyzeFederation`)
|
|
299
469
|
|
|
300
|
-
| Category |
|
|
301
|
-
|
|
302
|
-
| `versionConflicts` |
|
|
303
|
-
| `singletonMismatches` |
|
|
304
|
-
| `hostGaps` |
|
|
305
|
-
| `ghostShares` |
|
|
470
|
+
| Category | Severity | Description |
|
|
471
|
+
|----------|----------|-------------|
|
|
472
|
+
| `versionConflicts` | 🔴 HIGH | `requiredVersion` ranges across MFs have no overlap |
|
|
473
|
+
| `singletonMismatches` | 🟠 MEDIUM | `singleton: true` in some MFs, absent in others |
|
|
474
|
+
| `hostGaps` | 🟠 MEDIUM | Package used by 2+ MFs but not declared in `shared` by anyone |
|
|
475
|
+
| `ghostShares` | 🟡 LOW | Package in `shared` of one MF, unused/unshared by all others |
|
|
306
476
|
|
|
307
477
|
## Known limitations
|
|
308
478
|
|
|
309
|
-
- **TypeScript path aliases without `tsconfigPath`**: aliased imports are treated as external package names.
|
|
479
|
+
- **TypeScript path aliases without `tsconfigPath`**: aliased imports are treated as external package names.
|
|
310
480
|
- **Dynamic imports with variables** (`import(moduleName)`): not analysed — requires runtime information.
|
|
311
|
-
- **Exact tsconfig alias patterns** (non-wildcard
|
|
312
|
-
- **Subclassed `ModuleFederationPlugin`**: auto-extraction matches by constructor name
|
|
313
|
-
|
|
314
|
-
## Demo
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
npx tsx packages/shared-inspector/demo/run.ts
|
|
318
|
-
```
|
|
481
|
+
- **Exact tsconfig alias patterns** (non-wildcard): only `"@alias/*"` wildcard form is supported.
|
|
482
|
+
- **Subclassed `ModuleFederationPlugin`**: auto-extraction matches by constructor name — pass `sharedConfig` explicitly for custom subclasses.
|
|
319
483
|
|
|
320
484
|
## License
|
|
321
485
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CliArgs } from './types.js';
|
|
2
|
+
import type { SharedDepConfig } from '../types.js';
|
|
3
|
+
export declare function parseArgs(argv: string[]): CliArgs;
|
|
4
|
+
export declare function parseSharedValue(value: string): Record<string, SharedDepConfig>;
|
|
5
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAuFjD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAS/E"}
|