@mf-toolkit/shared-inspector 0.3.2 → 0.4.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 (66) hide show
  1. package/README.md +195 -127
  2. package/dist/cli/args.d.ts +5 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +98 -0
  5. package/dist/cli/args.js.map +1 -0
  6. package/dist/cli/colorize-report.d.ts +6 -0
  7. package/dist/cli/colorize-report.d.ts.map +1 -0
  8. package/dist/cli/colorize-report.js +71 -0
  9. package/dist/cli/colorize-report.js.map +1 -0
  10. package/dist/cli/colors.d.ts +14 -0
  11. package/dist/cli/colors.d.ts.map +1 -0
  12. package/dist/cli/colors.js +26 -0
  13. package/dist/cli/colors.js.map +1 -0
  14. package/dist/cli/help.d.ts +2 -0
  15. package/dist/cli/help.d.ts.map +1 -0
  16. package/dist/cli/help.js +30 -0
  17. package/dist/cli/help.js.map +1 -0
  18. package/dist/cli/index.d.ts +8 -0
  19. package/dist/cli/index.d.ts.map +1 -0
  20. package/dist/cli/index.js +62 -0
  21. package/dist/cli/index.js.map +1 -0
  22. package/dist/cli/interactive.d.ts +8 -0
  23. package/dist/cli/interactive.d.ts.map +1 -0
  24. package/dist/cli/interactive.js +41 -0
  25. package/dist/cli/interactive.js.map +1 -0
  26. package/dist/cli/run-federation.d.ts +3 -0
  27. package/dist/cli/run-federation.d.ts.map +1 -0
  28. package/dist/cli/run-federation.js +41 -0
  29. package/dist/cli/run-federation.js.map +1 -0
  30. package/dist/cli/run-project.d.ts +5 -0
  31. package/dist/cli/run-project.d.ts.map +1 -0
  32. package/dist/cli/run-project.js +57 -0
  33. package/dist/cli/run-project.js.map +1 -0
  34. package/dist/cli/spinner.d.ts +10 -0
  35. package/dist/cli/spinner.d.ts.map +1 -0
  36. package/dist/cli/spinner.js +45 -0
  37. package/dist/cli/spinner.js.map +1 -0
  38. package/dist/cli/types.d.ts +17 -0
  39. package/dist/cli/types.d.ts.map +1 -0
  40. package/dist/cli/types.js +2 -0
  41. package/dist/cli/types.js.map +1 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +13 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/reporter/diagnostics.d.ts +23 -0
  51. package/dist/reporter/diagnostics.d.ts.map +1 -0
  52. package/dist/reporter/diagnostics.js +138 -0
  53. package/dist/reporter/diagnostics.js.map +1 -0
  54. package/dist/reporter/format-federation-report.d.ts +11 -5
  55. package/dist/reporter/format-federation-report.d.ts.map +1 -1
  56. package/dist/reporter/format-federation-report.js +78 -37
  57. package/dist/reporter/format-federation-report.js.map +1 -1
  58. package/dist/reporter/format-report.d.ts +14 -7
  59. package/dist/reporter/format-report.d.ts.map +1 -1
  60. package/dist/reporter/format-report.js +67 -38
  61. package/dist/reporter/format-report.js.map +1 -1
  62. package/dist/reporter/scoring.d.ts +28 -0
  63. package/dist/reporter/scoring.d.ts.map +1 -0
  64. package/dist/reporter/scoring.js +74 -0
  65. package/dist/reporter/scoring.js.map +1 -0
  66. package/package.json +5 -2
package/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
  [![license](https://img.shields.io/npm/l/@mf-toolkit/shared-inspector?color=blue)](https://github.com/zvitaly7/mf-toolkit/blob/main/LICENSE)
5
5
  [![node](https://img.shields.io/node/v/@mf-toolkit/shared-inspector?color=339933&logo=node.js)](https://nodejs.org)
6
6
 
7
- Build-time analyser for Module Federation `shared` dependencies. Two-phase architecture: **collect facts → analyse facts**.
7
+ **Stop debugging Module Federation in production.**
8
+
9
+ `shared` config breaks in silence — wrong versions ship, 10× React ends up in the bundle, singleton chains collapse, 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
 
@@ -22,43 +24,171 @@ Existing tools (webpack-bundle-analyzer, source-map-explorer) show *what ended u
22
24
  npm install --save-dev @mf-toolkit/shared-inspector
23
25
  ```
24
26
 
27
+ ## CLI
28
+
29
+ The fastest way to get started — no config file, no webpack required:
30
+
31
+ ```bash
32
+ # Analyse the current project (auto-reads name from package.json)
33
+ npx @mf-toolkit/shared-inspector
34
+
35
+ # Step-by-step interactive wizard — answers guide you through all options
36
+ npx @mf-toolkit/shared-inspector --interactive
37
+
38
+ # Pass options directly
39
+ npx @mf-toolkit/shared-inspector --source ./src --shared react,react-dom --fail-on mismatch
40
+
41
+ # Load shared config from a JSON file
42
+ npx @mf-toolkit/shared-inspector --shared ./shared-config.json --write-manifest
43
+
44
+ # Cross-MF federation analysis from saved manifests
45
+ npx @mf-toolkit/shared-inspector federation checkout.json catalog.json cart.json
46
+ ```
47
+
48
+ ### Interactive wizard
49
+
50
+ ```
51
+ $ npx @mf-toolkit/shared-inspector --interactive
52
+
53
+ [MfSharedInspector] Interactive setup
54
+
55
+ Source directories to scan (default: ./src):
56
+ Scan depth — direct or local-graph (default: local-graph):
57
+ Shared packages — comma-separated names or path to .json (empty to skip): react,react-dom,mobx
58
+ Path to tsconfig.json for alias resolution (empty to skip):
59
+ Workspace packages to exclude, comma-separated (empty to skip):
60
+ Fail build on findings — mismatch / unused / any / none (default: none): mismatch
61
+ Write project-manifest.json? (y/N): n
62
+ ```
63
+
64
+ ### CLI reference
65
+
66
+ | Flag | Default | Description |
67
+ |------|---------|-------------|
68
+ | `--source, -s <dirs>` | `./src` | Source dirs to scan, comma-separated |
69
+ | `--depth <depth>` | `local-graph` | Scan depth: `direct` \| `local-graph` |
70
+ | `--shared <packages\|file>` | — | Comma-separated package names or path to `.json` config |
71
+ | `--tsconfig <path>` | — | tsconfig.json for path alias resolution |
72
+ | `--workspace-packages <pkgs>` | — | Comma-separated workspace packages to exclude |
73
+ | `--name <name>` | auto from `package.json` | Project name |
74
+ | `--fail-on <rule>` | — | Exit 1 when findings match: `mismatch` \| `unused` \| `any` |
75
+ | `--write-manifest` | `false` | Write `project-manifest.json` to output dir |
76
+ | `--output-dir <dir>` | `.` | Output directory for manifest |
77
+ | `--interactive, -i` | — | Launch step-by-step wizard |
78
+ | `--version, -v` | — | Print version and exit |
79
+ | `--help, -h` | — | Show help |
80
+
81
+ ## Terminal output
82
+
83
+ Each finding is rendered as a diagnostic card: what's wrong, what breaks at runtime, and a ready-to-paste fix.
84
+
85
+ ```
86
+ mf-inspector v0.4.0
87
+
88
+ ✓ Scanned 47 files
89
+
90
+ [MfSharedInspector] checkout (depth: local-graph, 47 files scanned)
91
+ ────────────────────────────────────────────────────────────
92
+
93
+ ⚠ Version Mismatch — react
94
+ configured: ^18.0.0 | installed: 17.0.2
95
+ → Risk: Invalid hook call, broken context across MFs
96
+ 💡 Fix:
97
+ shared: {
98
+ react: { singleton: true, requiredVersion: "^18.0.0" }
99
+ }
100
+
101
+ ✗ Unused Shared — lodash
102
+ 0 imports, shared without singleton
103
+ → Wastes bundle negotiation overhead with no benefit
104
+ 💡 Fix: Remove "lodash" from shared config
105
+
106
+ → Not Shared — mobx (12 imports in 8 files via re-export in src/shared/index.ts)
107
+ → Risk: Each MF gets its own MobX — observables and reactions won't sync between MFs
108
+ 💡 Fix:
109
+ shared: {
110
+ mobx: { singleton: true }
111
+ }
112
+
113
+ ────────────────────────────────────────────────────────────
114
+ Score: 62/100 🟠 RISKY
115
+
116
+ Issues:
117
+ 🔴 1 high — version mismatch
118
+ 🟠 1 medium — singleton gaps, duplicate libs
119
+ 🟡 1 low — over-sharing
120
+
121
+ Total: 12 shared, 10 used, 1 unused, 1 candidates, 1 mismatch, 0 eager risks
122
+ ```
123
+
124
+ Colors are auto-applied in TTY terminals and disabled in CI / piped output (`NO_COLOR` / `TERM=dumb` respected).
125
+
126
+ ## Risk scoring
127
+
128
+ Every report ends with a score out of 100:
129
+
130
+ | Severity | Penalty | Covers |
131
+ |----------|---------|--------|
132
+ | 🔴 HIGH | −20 each | Version mismatches, cross-MF version conflicts |
133
+ | 🟠 MEDIUM | −8 each | Singleton risks, eager risks, duplicate libs, host gaps |
134
+ | 🟡 LOW | −3 each | Unused shared packages, ghost shares |
135
+
136
+ | Score | Label |
137
+ |-------|-------|
138
+ | 90–100 | ✅ HEALTHY |
139
+ | 70–89 | 🟡 GOOD |
140
+ | 40–69 | 🟠 RISKY |
141
+ | 0–39 | 🔴 CRITICAL |
142
+
143
+ Use `scoreProjectReport` / `scoreFederationReport` programmatically to integrate with dashboards or custom CI gates:
144
+
145
+ ```typescript
146
+ import { analyzeProject, scoreProjectReport } from '@mf-toolkit/shared-inspector';
147
+
148
+ const report = analyzeProject(manifest);
149
+ const { score, label, high, medium, low } = scoreProjectReport(report);
150
+
151
+ if (score < 70) {
152
+ console.error(`Shared config score: ${score}/100 (${label})`);
153
+ process.exit(1);
154
+ }
155
+ ```
156
+
25
157
  ## Quick start
26
158
 
27
159
  ### Programmatic API (two-phase)
28
160
 
29
161
  ```typescript
30
- import { buildProjectManifest, analyzeProject } from '@mf-toolkit/shared-inspector';
162
+ import { buildProjectManifest, analyzeProject, formatReport } from '@mf-toolkit/shared-inspector';
31
163
 
32
164
  // Phase 1: collect facts
33
165
  const manifest = await buildProjectManifest({
34
166
  name: 'checkout',
35
167
  sourceDirs: ['./src'],
36
168
  sharedConfig: {
37
- react: { singleton: true, requiredVersion: '^19.0.0' },
38
- 'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
169
+ react: { singleton: true, requiredVersion: '^18.0.0' },
170
+ 'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
39
171
  lodash: {},
40
172
  },
41
- // depth: 'local-graph' ← default, follows barrel re-exports
42
- // tsconfigPath: './tsconfig.json' ← optional, resolves @alias/* imports
173
+ // depth: 'local-graph' ← default, follows barrel re-exports
174
+ // tsconfigPath: './tsconfig.json' ← optional, resolves @alias/* imports
43
175
  // workspacePackages: ['@my-org/*'] ← optional, excludes local monorepo packages
44
176
  });
45
177
 
46
178
  // Phase 2: analyse facts
47
- const report = analyzeProject(manifest, {
48
- alwaysShared: ['react', 'react-dom'],
49
- });
179
+ const report = analyzeProject(manifest);
50
180
 
51
- console.log(report.unused);
52
- // [{ package: 'lodash', singleton: false }]
181
+ console.log(report.mismatched);
182
+ // [{ package: 'react', configured: '^18.0.0', installed: '17.0.2' }]
53
183
 
54
184
  console.log(report.candidates);
55
185
  // [{ package: 'mobx', importCount: 12, via: 'reexport', files: ['src/shared/index.ts'] }]
56
186
 
57
- console.log(report.mismatched);
58
- // [{ package: 'react', configured: '^19.0.0', installed: '18.3.1' }]
187
+ console.log(report.unused);
188
+ // [{ package: 'lodash', singleton: false }]
59
189
 
60
- console.log(report.eagerRisks);
61
- // [{ package: 'react-dom' }] ← eager: true without singleton: true
190
+ // Human-readable output with risk descriptions and fix snippets
191
+ console.log(formatReport(report, { name: manifest.project.name }));
62
192
  ```
63
193
 
64
194
  ### Shortcut API
@@ -97,15 +227,6 @@ module.exports = {
97
227
  };
98
228
  ```
99
229
 
100
- Pass `sharedConfig` explicitly to override auto-extraction:
101
-
102
- ```typescript
103
- new MfSharedInspectorPlugin({
104
- sourceDirs: ['./src'],
105
- sharedConfig: { react: { singleton: true, requiredVersion: '^18.0.0' } },
106
- })
107
- ```
108
-
109
230
  ## Analysis depth
110
231
 
111
232
  | Depth | What it finds | Speed |
@@ -119,113 +240,62 @@ The difference matters when your project uses barrel files:
119
240
  // src/shared/index.ts
120
241
  export { observer } from 'mobx-react'; // re-export
121
242
  export { makeAutoObservable } from 'mobx'; // re-export
122
- ```
123
243
 
124
- ```ts
125
244
  // src/app.tsx
126
245
  import { observer } from './shared'; // relative import — direct mode stops here
127
246
  ```
128
247
 
129
- - **`depth: 'direct'`** scans `app.tsx`, sees `./shared` (relative) → skips. `mobx` not found.
130
- - **`depth: 'local-graph'`** follows `./shared` → `shared/index.ts` → finds `mobx` and `mobx-react` via re-export.
248
+ - **`depth: 'direct'`** sees `./shared` (relative) → skips. `mobx` not found.
249
+ - **`depth: 'local-graph'`** follows `./shared` → finds `mobx` and `mobx-react` via re-export.
131
250
 
132
251
  ## TypeScript path aliases
133
252
 
134
- When your project uses `paths` in `tsconfig.json`, pass `tsconfigPath` so the traverser follows aliased imports into local files instead of treating them as external packages:
135
-
136
253
  ```typescript
137
254
  // tsconfig.json
138
- { "compilerOptions": { "baseUrl": ".", "paths": { "@components/*": ["src/components/*"] } } }
255
+ { "compilerOptions": { "paths": { "@components/*": ["src/components/*"] } } }
139
256
 
140
- // src/app.tsx
141
- import { Button } from '@components/Button'; // ← followed as local file, not external package
142
- ```
143
-
144
- ```typescript
145
257
  await buildProjectManifest({
146
258
  sourceDirs: ['./src'],
147
259
  tsconfigPath: './tsconfig.json', // enables alias resolution
148
260
  });
149
261
  ```
150
262
 
151
- Without `tsconfigPath`, `@components/Button` is treated as an external package name and packages imported inside it are invisible in local-graph mode.
152
-
153
- ## Workspace packages
154
-
155
- In a monorepo where local packages import each other, use `workspacePackages` to prevent internal packages from appearing in `resolvedPackages`:
156
-
157
- ```typescript
158
- await buildProjectManifest({
159
- sourceDirs: ['./src'],
160
- workspacePackages: ['@my-org/design-system', '@my-org/*'],
161
- });
162
- ```
163
-
164
- ## Terminal output
165
-
166
- ```
167
- [MfSharedInspector] checkout (depth: local-graph, 47 files scanned)
168
-
169
- Version mismatch (sharing silently broken):
170
- ⚠ react — requires ^19.0.0, installed 18.3.1
171
-
172
- Unused shared (safe to remove):
173
- ✗ lodash — 0 imports, shared without singleton
174
- ✗ @tanstack/react-query — 0 imports, shared as singleton
175
-
176
- Candidates (consider adding to shared):
177
- → mobx (12 imports in 8 files, via re-export in src/shared/index.ts)
178
- → react-router-dom (6 imports in 4 files)
179
-
180
- Singleton risks (add singleton: true):
181
- ⚠ react-router-dom — manages global state, singleton: true recommended
182
-
183
- Eager risks (add singleton: true or remove eager: true):
184
- ⚠ react-dom — eager: true without singleton: true, risk of duplicate instances
185
-
186
- Total: 12 shared, 10 used, 2 unused, 2 candidates, 1 mismatch, 1 eager risks
187
- ```
263
+ Without `tsconfigPath`, `@components/Button` is treated as an external package and packages imported inside it are invisible in `local-graph` mode.
188
264
 
189
265
  ## CI pipeline: project → federation
190
266
 
191
- Each microfrontend generates a manifest as a build artifact:
267
+ Each microfrontend generates a manifest as a build artifact, then they're aggregated for cross-team analysis:
192
268
 
193
269
  ```yaml
270
+ # .github/workflows/build.yml
194
271
  jobs:
195
272
  build-checkout:
196
273
  steps:
197
274
  - run: npm run build # MfSharedInspectorPlugin writes project-manifest.json
198
275
  - uses: actions/upload-artifact@v4
199
- with:
200
- name: manifest-checkout
201
- path: project-manifest.json
276
+ with: { name: manifest-checkout, path: project-manifest.json }
202
277
  ```
203
278
 
204
- Each MF manifest can then be aggregated for cross-team analysis:
205
-
206
279
  ```typescript
207
- import { analyzeFederation, formatFederationReport } from '@mf-toolkit/shared-inspector';
280
+ import { analyzeFederation, formatFederationReport, scoreFederationReport } from '@mf-toolkit/shared-inspector';
208
281
 
209
282
  const report = analyzeFederation([checkoutManifest, catalogManifest, cartManifest]);
210
- console.log(formatFederationReport(report));
211
- ```
283
+ const { score, label } = scoreFederationReport(report);
212
284
 
285
+ console.log(formatFederationReport(report));
286
+ // ⚠ Version Conflict — react
287
+ // checkout: ^17.0.0
288
+ // catalog: ^18.0.0
289
+ // → Risk: MF singleton negotiation will silently load wrong version → Invalid hook call
290
+ // 💡 Fix: shared: { react: { singleton: true, requiredVersion: "^18.0.0" } }
291
+ //
292
+ // Score: 60/100 🟠 RISKY
213
293
  ```
214
- [MfSharedInspector] federation analysis (3 MFs)
215
-
216
- Version conflicts (singleton negotiation will fail):
217
- ⚠ react — checkout: ^17.0.0, catalog: ^18.0.0
218
294
 
219
- Singleton mismatches (add singleton: true to all MFs):
220
- ⚠ mobx — singleton in [checkout], not singleton in [catalog, cart]
295
+ Or use the CLI directly:
221
296
 
222
- Host gaps (add to shared — each MF bundles its own copy):
223
- axios used by [checkout, catalog], not in shared
224
-
225
- Ghost shares (remove from shared — no other MF benefits):
226
- ✗ lodash — shared only by cart, unused by all other MFs
227
-
228
- Total: 3 MFs, 1 version conflicts, 1 singleton mismatches, 1 host gaps, 1 ghost shares
297
+ ```bash
298
+ npx @mf-toolkit/shared-inspector federation checkout.json catalog.json cart.json
229
299
  ```
230
300
 
231
301
  ## API reference
@@ -253,6 +323,20 @@ console.log(formatFederationReport(report));
253
323
  | `additionalCandidates` | `string[]` | `[]` | Extend built-in candidates list |
254
324
  | `additionalSingletonRisks` | `string[]` | `[]` | Extend built-in singleton-risk list |
255
325
 
326
+ ### `scoreProjectReport(report)` / `scoreFederationReport(report)`
327
+
328
+ Returns a `RiskScore`:
329
+
330
+ ```typescript
331
+ interface RiskScore {
332
+ score: number; // 0–100, higher is better
333
+ label: 'HEALTHY' | 'GOOD' | 'RISKY' | 'CRITICAL';
334
+ high: number; // count of high-severity findings
335
+ medium: number; // count of medium-severity findings
336
+ low: number; // count of low-severity findings
337
+ }
338
+ ```
339
+
256
340
  ### `analyzeFederation(manifests, options?)`
257
341
 
258
342
  Accepts N `ProjectManifest` objects (one per microfrontend) and returns a `FederationReport`.
@@ -261,20 +345,12 @@ Accepts N `ProjectManifest` objects (one per microfrontend) and returns a `Feder
261
345
  |--------|------|---------|-------------|
262
346
  | `alwaysShared` | `string[]` | `['react','react-dom']` | Exclude from ghost/gap detection |
263
347
 
264
- ### `formatFederationReport(report)`
265
-
266
- Formats a `FederationReport` as a human-readable terminal string.
267
-
268
348
  ### `MfSharedInspectorPlugin` options
269
349
 
270
350
  Extends all `buildProjectManifest` options (except `name`, auto-resolved from compiler) plus:
271
351
 
272
352
  | Option | Type | Default | Description |
273
353
  |--------|------|---------|-------------|
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
354
  | `warn` | `boolean` | `true` | Print findings to console |
279
355
  | `failOn` | `'mismatch' \| 'unused' \| 'any'` | `undefined` | Fail the build when findings match |
280
356
  | `writeManifest` | `boolean` | `false` | Write `project-manifest.json` to `outputDir` |
@@ -285,37 +361,29 @@ Extends all `buildProjectManifest` options (except `name`, auto-resolved from co
285
361
 
286
362
  ### Per-project (`analyzeProject`)
287
363
 
288
- | Category | Type | Description |
289
- |----------|------|-------------|
290
- | `mismatched` | Deterministic | `requiredVersion` doesn't satisfy installed version |
291
- | `unused` | Deterministic* | In `shared` config but not observed in scanned sources |
292
- | `candidates` | Heuristic | Observed packages not in `shared` that are typically shared |
293
- | `singletonRisks` | Heuristic | Global-state packages shared without `singleton: true` |
294
- | `eagerRisks` | Heuristic | `eager: true` without `singleton: true` risk of duplicate instances |
295
-
296
- *Within the visibility of the chosen depth.*
364
+ | Category | Severity | Description |
365
+ |----------|----------|-------------|
366
+ | `mismatched` | 🔴 HIGH | `requiredVersion` doesn't satisfy installed version |
367
+ | `singletonRisks` | 🟠 MEDIUM | Global-state packages shared without `singleton: true` |
368
+ | `eagerRisks` | 🟠 MEDIUM | `eager: true` without `singleton: true` |
369
+ | `candidates` | 🟠 MEDIUM | Used packages missing from `shared` (each MF bundles own copy) |
370
+ | `unused` | 🟡 LOW | In `shared` config but not observed in scanned sources |
297
371
 
298
372
  ### Cross-MF (`analyzeFederation`)
299
373
 
300
- | Category | Type | Description |
301
- |----------|------|-------------|
302
- | `versionConflicts` | Deterministic | `requiredVersion` ranges across MFs have no overlap |
303
- | `singletonMismatches` | Deterministic | `singleton: true` in some MFs, absent in others |
304
- | `hostGaps` | Heuristic | Package used by 2+ MFs but not declared in `shared` by anyone |
305
- | `ghostShares` | Heuristic | Package in `shared` of one MF, unused/unshared by all others |
374
+ | Category | Severity | Description |
375
+ |----------|----------|-------------|
376
+ | `versionConflicts` | 🔴 HIGH | `requiredVersion` ranges across MFs have no overlap |
377
+ | `singletonMismatches` | 🟠 MEDIUM | `singleton: true` in some MFs, absent in others |
378
+ | `hostGaps` | 🟠 MEDIUM | Package used by 2+ MFs but not declared in `shared` by anyone |
379
+ | `ghostShares` | 🟡 LOW | Package in `shared` of one MF, unused/unshared by all others |
306
380
 
307
381
  ## Known limitations
308
382
 
309
- - **TypeScript path aliases without `tsconfigPath`**: aliased imports are treated as external package names. Pass `tsconfigPath` to resolve them correctly.
383
+ - **TypeScript path aliases without `tsconfigPath`**: aliased imports are treated as external package names.
310
384
  - **Dynamic imports with variables** (`import(moduleName)`): not analysed — requires runtime information.
311
- - **Exact tsconfig alias patterns** (non-wildcard, e.g. `"@root": ["."]`): not supported, only `"@alias/*"` wildcard form.
312
- - **Subclassed `ModuleFederationPlugin`**: auto-extraction matches by constructor name. A custom subclass (`class MyMFP extends ModuleFederationPlugin`) won't be detected — pass `sharedConfig` explicitly in that case.
313
-
314
- ## Demo
315
-
316
- ```bash
317
- npx tsx packages/shared-inspector/demo/run.ts
318
- ```
385
+ - **Exact tsconfig alias patterns** (non-wildcard): only `"@alias/*"` wildcard form is supported.
386
+ - **Subclassed `ModuleFederationPlugin`**: auto-extraction matches by constructor name — pass `sharedConfig` explicitly for custom subclasses.
319
387
 
320
388
  ## License
321
389
 
@@ -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"}
@@ -0,0 +1,98 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const VALID_DEPTHS = new Set(['direct', 'local-graph']);
4
+ const VALID_FAIL_ON = new Set(['mismatch', 'unused', 'any']);
5
+ export function parseArgs(argv) {
6
+ const args = {
7
+ command: 'project',
8
+ interactive: false,
9
+ sourceDirs: [],
10
+ depth: 'local-graph',
11
+ workspacePackages: [],
12
+ writeManifest: false,
13
+ outputDir: '.',
14
+ manifestFiles: [],
15
+ };
16
+ if (argv[0] === 'federation') {
17
+ args.command = 'federation';
18
+ for (let i = 1; i < argv.length; i++) {
19
+ if (argv[i] === '--help' || argv[i] === '-h') {
20
+ args.command = 'help';
21
+ break;
22
+ }
23
+ if (!argv[i].startsWith('-')) {
24
+ args.manifestFiles.push(argv[i]);
25
+ }
26
+ }
27
+ return args;
28
+ }
29
+ for (let i = 0; i < argv.length; i++) {
30
+ const arg = argv[i];
31
+ switch (arg) {
32
+ case '--help':
33
+ case '-h':
34
+ args.command = 'help';
35
+ break;
36
+ case '--version':
37
+ case '-v':
38
+ args.command = 'version';
39
+ break;
40
+ case '--interactive':
41
+ case '-i':
42
+ args.interactive = true;
43
+ break;
44
+ case '--source':
45
+ case '-s':
46
+ args.sourceDirs.push(...(argv[++i] ?? '').split(',').filter(Boolean));
47
+ break;
48
+ case '--depth': {
49
+ const val = argv[++i] ?? '';
50
+ if (!VALID_DEPTHS.has(val)) {
51
+ throw new Error(`Invalid --depth value "${val}". Expected: direct | local-graph`);
52
+ }
53
+ args.depth = val;
54
+ break;
55
+ }
56
+ case '--shared':
57
+ args.sharedConfig = parseSharedValue(argv[++i] ?? '');
58
+ break;
59
+ case '--tsconfig':
60
+ args.tsconfigPath = argv[++i];
61
+ break;
62
+ case '--workspace-packages':
63
+ args.workspacePackages.push(...(argv[++i] ?? '').split(',').filter(Boolean));
64
+ break;
65
+ case '--fail-on': {
66
+ const val = argv[++i] ?? '';
67
+ if (!VALID_FAIL_ON.has(val)) {
68
+ throw new Error(`Invalid --fail-on value "${val}". Expected: mismatch | unused | any`);
69
+ }
70
+ args.failOn = val;
71
+ break;
72
+ }
73
+ case '--write-manifest':
74
+ args.writeManifest = true;
75
+ break;
76
+ case '--output-dir':
77
+ args.outputDir = argv[++i] ?? '.';
78
+ break;
79
+ case '--name':
80
+ args.name = argv[++i];
81
+ break;
82
+ }
83
+ }
84
+ if (!args.interactive && args.sourceDirs.length === 0) {
85
+ args.sourceDirs = ['./src'];
86
+ }
87
+ return args;
88
+ }
89
+ export function parseSharedValue(value) {
90
+ if (!value)
91
+ return {};
92
+ if (value.endsWith('.json')) {
93
+ const content = readFileSync(resolve(process.cwd(), value), 'utf-8');
94
+ return JSON.parse(content);
95
+ }
96
+ return Object.fromEntries(value.split(',').filter(Boolean).map((p) => [p.trim(), {}]));
97
+ }
98
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AACxD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AAE7D,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,IAAI,GAAY;QACpB,OAAO,EAAE,SAAS;QAClB,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,EAAE;QACd,KAAK,EAAE,aAAa;QACpB,iBAAiB,EAAE,EAAE;QACrB,aAAa,EAAE,KAAK;QACpB,SAAS,EAAE,GAAG;QACd,aAAa,EAAE,EAAE;KAClB,CAAC;IAEF,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC7C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;gBACtB,MAAM;YACR,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ,CAAC;YACd,KAAK,IAAI;gBACP,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;gBACtB,MAAM;YACR,KAAK,WAAW,CAAC;YACjB,KAAK,IAAI;gBACP,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;gBACzB,MAAM;YACR,KAAK,eAAe,CAAC;YACrB,KAAK,IAAI;gBACP,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,MAAM;YACR,KAAK,UAAU,CAAC;YAChB,KAAK,IAAI;gBACP,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;gBACtE,MAAM;YACR,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC3B,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,mCAAmC,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,CAAC,KAAK,GAAG,GAA+B,CAAC;gBAC7C,MAAM;YACR,CAAC;YACD,KAAK,UAAU;gBACb,IAAI,CAAC,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtD,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC9B,MAAM;YACR,KAAK,sBAAsB;gBACzB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC7E,MAAM;YACR,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,sCAAsC,CAAC,CAAC;gBACzF,CAAC;gBACD,IAAI,CAAC,MAAM,GAAG,GAAoC,CAAC;gBACnD,MAAM;YACR,CAAC;YACD,KAAK,kBAAkB;gBACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC1B,MAAM;YACR,KAAK,cAAc;gBACjB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC;gBAClC,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtB,MAAM;QACV,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoC,CAAC;IAChE,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CACvB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAC5D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Adds ANSI colors to a formatted report string.
3
+ * Returns the original string unchanged when stdout is not a TTY.
4
+ */
5
+ export declare function colorizeReport(text: string): string;
6
+ //# sourceMappingURL=colorize-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colorize-report.d.ts","sourceRoot":"","sources":["../../src/cli/colorize-report.ts"],"names":[],"mappings":"AAmDA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD"}
@@ -0,0 +1,71 @@
1
+ // ─── Report colorizer ─────────────────────────────────────────────────────────
2
+ //
3
+ // Post-processes the plain-text output of formatReport / formatFederationReport,
4
+ // adding ANSI colors line-by-line.
5
+ //
6
+ // Separated from the reporters so the library API stays color-free
7
+ // (useful for programmatic consumers, JSON pipelines, etc.)
8
+ import { isTTY, bold, dim, red, yellow, green, cyan, gray, combine } from './colors.js';
9
+ // ─── Line matchers ────────────────────────────────────────────────────────────
10
+ function colorizeLine(line) {
11
+ // ── Issue titles ────────────────────────────────────────────────────────────
12
+ if (/^⚠ Version (Mismatch|Conflict)/.test(line))
13
+ return combine(bold, red)(line);
14
+ if (/^⚠ (Singleton Risk|Singleton Mismatch|Eager Risk)/.test(line))
15
+ return combine(bold, yellow)(line);
16
+ if (/^→ (Not Shared|Host Gap)/.test(line))
17
+ return combine(bold, cyan)(line);
18
+ if (/^✗ (Unused Shared|Ghost Share)/.test(line))
19
+ return dim(line);
20
+ // ── Card detail lines ───────────────────────────────────────────────────────
21
+ if (/→ Risk:/.test(line))
22
+ return line.replace('→ Risk:', red('→ Risk:'));
23
+ if (/💡 Fix:/.test(line))
24
+ return green(line);
25
+ if (/Remove ".+" from/.test(line))
26
+ return green(line);
27
+ // ── Score block ─────────────────────────────────────────────────────────────
28
+ if (/^Score:\s+\d+\/100/.test(line)) {
29
+ if (line.includes('CRITICAL'))
30
+ return combine(bold, red)(line);
31
+ if (line.includes('RISKY'))
32
+ return combine(bold, yellow)(line);
33
+ if (line.includes('GOOD'))
34
+ return combine(bold, cyan)(line);
35
+ if (line.includes('HEALTHY'))
36
+ return combine(bold, green)(line);
37
+ return bold(line);
38
+ }
39
+ // ── Section markers ─────────────────────────────────────────────────────────
40
+ if (/^\[MfSharedInspector\]/.test(line))
41
+ return bold(line);
42
+ if (/^[─]{10,}/.test(line))
43
+ return gray(line);
44
+ if (/^Total:/.test(line))
45
+ return dim(line);
46
+ if (/^Issues:/.test(line))
47
+ return bold(line);
48
+ // ── Score issue rows ─────────────────────────────────────────────────────────
49
+ if (/🔴/.test(line))
50
+ return line; // emoji already colored in supported terminals
51
+ if (/🟠/.test(line))
52
+ return line;
53
+ if (/🟡/.test(line))
54
+ return line;
55
+ // ── Clean / no-issues ────────────────────────────────────────────────────────
56
+ if (/✓\s+No issues/.test(line))
57
+ return combine(bold, green)(line);
58
+ if (/✓\s+No federation/.test(line))
59
+ return combine(bold, green)(line);
60
+ return line;
61
+ }
62
+ /**
63
+ * Adds ANSI colors to a formatted report string.
64
+ * Returns the original string unchanged when stdout is not a TTY.
65
+ */
66
+ export function colorizeReport(text) {
67
+ if (!isTTY)
68
+ return text;
69
+ return text.split('\n').map(colorizeLine).join('\n');
70
+ }
71
+ //# sourceMappingURL=colorize-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colorize-report.js","sourceRoot":"","sources":["../../src/cli/colorize-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare const isTTY: boolean;
2
+ export declare const bold: (s: string) => string;
3
+ export declare const dim: (s: string) => string;
4
+ export declare const italic: (s: string) => string;
5
+ export declare const red: (s: string) => string;
6
+ export declare const yellow: (s: string) => string;
7
+ export declare const green: (s: string) => string;
8
+ export declare const cyan: (s: string) => string;
9
+ export declare const blue: (s: string) => string;
10
+ export declare const magenta: (s: string) => string;
11
+ export declare const gray: (s: string) => string;
12
+ /** Combine two color transforms. */
13
+ export declare function combine(...fns: Array<(s: string) => string>): (s: string) => string;
14
+ //# sourceMappingURL=colors.d.ts.map