@mf-toolkit/shared-inspector 0.1.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 (70) hide show
  1. package/README.md +254 -0
  2. package/dist/analyzer/analyze-project.d.ts +9 -0
  3. package/dist/analyzer/analyze-project.d.ts.map +1 -0
  4. package/dist/analyzer/analyze-project.js +38 -0
  5. package/dist/analyzer/analyze-project.js.map +1 -0
  6. package/dist/analyzer/detect-issues.d.ts +33 -0
  7. package/dist/analyzer/detect-issues.d.ts.map +1 -0
  8. package/dist/analyzer/detect-issues.js +69 -0
  9. package/dist/analyzer/detect-issues.js.map +1 -0
  10. package/dist/analyzer/policy.d.ts +27 -0
  11. package/dist/analyzer/policy.d.ts.map +1 -0
  12. package/dist/analyzer/policy.js +84 -0
  13. package/dist/analyzer/policy.js.map +1 -0
  14. package/dist/collector/build-project-manifest.d.ts +10 -0
  15. package/dist/collector/build-project-manifest.d.ts.map +1 -0
  16. package/dist/collector/build-project-manifest.js +82 -0
  17. package/dist/collector/build-project-manifest.js.map +1 -0
  18. package/dist/collector/collect-imports.d.ts +17 -0
  19. package/dist/collector/collect-imports.d.ts.map +1 -0
  20. package/dist/collector/collect-imports.js +87 -0
  21. package/dist/collector/collect-imports.js.map +1 -0
  22. package/dist/collector/parse-declarations.d.ts +26 -0
  23. package/dist/collector/parse-declarations.d.ts.map +1 -0
  24. package/dist/collector/parse-declarations.js +134 -0
  25. package/dist/collector/parse-declarations.js.map +1 -0
  26. package/dist/collector/parse-shared-config.d.ts +12 -0
  27. package/dist/collector/parse-shared-config.d.ts.map +1 -0
  28. package/dist/collector/parse-shared-config.js +56 -0
  29. package/dist/collector/parse-shared-config.js.map +1 -0
  30. package/dist/collector/resolve-tsconfig-paths.d.ts +39 -0
  31. package/dist/collector/resolve-tsconfig-paths.d.ts.map +1 -0
  32. package/dist/collector/resolve-tsconfig-paths.js +89 -0
  33. package/dist/collector/resolve-tsconfig-paths.js.map +1 -0
  34. package/dist/collector/resolve-versions.d.ts +15 -0
  35. package/dist/collector/resolve-versions.d.ts.map +1 -0
  36. package/dist/collector/resolve-versions.js +49 -0
  37. package/dist/collector/resolve-versions.js.map +1 -0
  38. package/dist/collector/traverse-local-modules.d.ts +27 -0
  39. package/dist/collector/traverse-local-modules.d.ts.map +1 -0
  40. package/dist/collector/traverse-local-modules.js +119 -0
  41. package/dist/collector/traverse-local-modules.js.map +1 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +9 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/inspect.d.ts +12 -0
  47. package/dist/inspect.d.ts.map +1 -0
  48. package/dist/inspect.js +15 -0
  49. package/dist/inspect.js.map +1 -0
  50. package/dist/plugins/webpack.d.ts +40 -0
  51. package/dist/plugins/webpack.d.ts.map +1 -0
  52. package/dist/plugins/webpack.js +95 -0
  53. package/dist/plugins/webpack.js.map +1 -0
  54. package/dist/reporter/format-report.d.ts +18 -0
  55. package/dist/reporter/format-report.d.ts.map +1 -0
  56. package/dist/reporter/format-report.js +87 -0
  57. package/dist/reporter/format-report.js.map +1 -0
  58. package/dist/reporter/write-report.d.ts +12 -0
  59. package/dist/reporter/write-report.d.ts.map +1 -0
  60. package/dist/reporter/write-report.js +19 -0
  61. package/dist/reporter/write-report.js.map +1 -0
  62. package/dist/types.d.ts +179 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +3 -0
  65. package/dist/types.js.map +1 -0
  66. package/dist/webpack.d.ts +3 -0
  67. package/dist/webpack.d.ts.map +1 -0
  68. package/dist/webpack.js +2 -0
  69. package/dist/webpack.js.map +1 -0
  70. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,254 @@
1
+ # `@mf-toolkit/shared-inspector`
2
+
3
+ [![status](https://img.shields.io/badge/status-in_development-orange)](https://github.com/zvitaly7/mf-toolkit)
4
+ [![version](https://img.shields.io/badge/version-0.1.0_unreleased-lightgrey)](https://github.com/zvitaly7/mf-toolkit)
5
+ [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/zvitaly7/mf-toolkit/blob/main/LICENSE)
6
+ [![node](https://img.shields.io/badge/node-%E2%89%A518-339933?logo=node.js)](https://nodejs.org)
7
+
8
+ > ⚠️ **Work in progress.** This package is feature-complete and fully tested (179 tests) but has not yet been published to npm. The API is stable but may receive minor changes before the official release. Do not use in production until v0.1.0 is tagged.
9
+
10
+ Build-time analyser for Module Federation `shared` dependencies. Two-phase architecture: **collect facts → analyse facts**.
11
+
12
+ ## The problem
13
+
14
+ Module Federation teams manually manage `shared` config and make three kinds of mistakes:
15
+
16
+ - **Over-sharing** — packages listed in `shared` that the microfrontend never imports. Creates artificial version coupling between independent teams.
17
+ - **Under-sharing** — packages used by both host and remote but missing from `shared`. Each microfrontend bundles its own copy (10× React = 10× 130 KB).
18
+ - **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.
19
+
20
+ Existing tools (webpack-bundle-analyzer, source-map-explorer) show *what ended up in the bundle*, not *why shared config is suboptimal*. Different questions.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install --save-dev @mf-toolkit/shared-inspector
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ### Programmatic API (two-phase)
31
+
32
+ ```typescript
33
+ import { buildProjectManifest, analyzeProject } from '@mf-toolkit/shared-inspector';
34
+
35
+ // Phase 1: collect facts
36
+ const manifest = await buildProjectManifest({
37
+ name: 'checkout',
38
+ sourceDirs: ['./src'],
39
+ sharedConfig: {
40
+ react: { singleton: true, requiredVersion: '^19.0.0' },
41
+ 'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
42
+ lodash: {},
43
+ },
44
+ // depth: 'local-graph' ← default, follows barrel re-exports
45
+ // tsconfigPath: './tsconfig.json' ← optional, resolves @alias/* imports
46
+ // workspacePackages: ['@my-org/*'] ← optional, excludes local monorepo packages
47
+ });
48
+
49
+ // Phase 2: analyse facts
50
+ const report = analyzeProject(manifest, {
51
+ alwaysShared: ['react', 'react-dom'],
52
+ });
53
+
54
+ console.log(report.unused);
55
+ // [{ package: 'lodash', singleton: false }]
56
+
57
+ console.log(report.candidates);
58
+ // [{ package: 'mobx', importCount: 12, via: 'reexport', files: ['src/shared/index.ts'] }]
59
+
60
+ console.log(report.mismatched);
61
+ // [{ package: 'react', configured: '^19.0.0', installed: '18.3.1' }]
62
+
63
+ console.log(report.eagerRisks);
64
+ // [{ package: 'react-dom' }] ← eager: true without singleton: true
65
+ ```
66
+
67
+ ### Shortcut API
68
+
69
+ ```typescript
70
+ import { inspect } from '@mf-toolkit/shared-inspector';
71
+
72
+ const report = await inspect({
73
+ name: 'checkout',
74
+ sourceDirs: ['./src'],
75
+ sharedConfig: { /* ... */ },
76
+ });
77
+ ```
78
+
79
+ ### Webpack plugin
80
+
81
+ ```typescript
82
+ import { MfSharedInspectorPlugin } from '@mf-toolkit/shared-inspector/webpack';
83
+
84
+ module.exports = {
85
+ plugins: [
86
+ new ModuleFederationPlugin({
87
+ name: 'checkout',
88
+ shared: { react: { singleton: true }, mobx: { singleton: true } },
89
+ }),
90
+
91
+ new MfSharedInspectorPlugin({
92
+ sourceDirs: ['./src'],
93
+ sharedConfig: {
94
+ react: { singleton: true, requiredVersion: '^19.0.0' },
95
+ mobx: { singleton: true },
96
+ },
97
+ tsconfigPath: './tsconfig.json', // resolve @alias/* imports
98
+ warn: true,
99
+ writeManifest: true, // writes project-manifest.json for CI
100
+ }),
101
+ ],
102
+ };
103
+ ```
104
+
105
+ ## Analysis depth
106
+
107
+ | Depth | What it finds | Speed |
108
+ |-------|--------------|-------|
109
+ | `'direct'` | Explicit `import` / `require` statements | Fast (ms) |
110
+ | `'local-graph'` *(default)* | + packages reachable via barrel re-exports and local wrappers | Slower (seconds) |
111
+
112
+ The difference matters when your project uses barrel files:
113
+
114
+ ```ts
115
+ // src/shared/index.ts
116
+ export { observer } from 'mobx-react'; // re-export
117
+ export { makeAutoObservable } from 'mobx'; // re-export
118
+ ```
119
+
120
+ ```ts
121
+ // src/app.tsx
122
+ import { observer } from './shared'; // relative import — direct mode stops here
123
+ ```
124
+
125
+ - **`depth: 'direct'`** scans `app.tsx`, sees `./shared` (relative) → skips. `mobx` not found.
126
+ - **`depth: 'local-graph'`** follows `./shared` → `shared/index.ts` → finds `mobx` and `mobx-react` via re-export.
127
+
128
+ ## TypeScript path aliases
129
+
130
+ 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:
131
+
132
+ ```typescript
133
+ // tsconfig.json
134
+ { "compilerOptions": { "baseUrl": ".", "paths": { "@components/*": ["src/components/*"] } } }
135
+
136
+ // src/app.tsx
137
+ import { Button } from '@components/Button'; // ← followed as local file, not external package
138
+ ```
139
+
140
+ ```typescript
141
+ await buildProjectManifest({
142
+ sourceDirs: ['./src'],
143
+ tsconfigPath: './tsconfig.json', // enables alias resolution
144
+ });
145
+ ```
146
+
147
+ Without `tsconfigPath`, `@components/Button` is treated as an external package name and packages imported inside it are invisible in local-graph mode.
148
+
149
+ ## Workspace packages
150
+
151
+ In a monorepo where local packages import each other, use `workspacePackages` to prevent internal packages from appearing in `resolvedPackages`:
152
+
153
+ ```typescript
154
+ await buildProjectManifest({
155
+ sourceDirs: ['./src'],
156
+ workspacePackages: ['@my-org/design-system', '@my-org/*'],
157
+ });
158
+ ```
159
+
160
+ ## Terminal output
161
+
162
+ ```
163
+ [MfSharedInspector] checkout (depth: local-graph, 47 files scanned)
164
+
165
+ Version mismatch (sharing silently broken):
166
+ ⚠ react — requires ^19.0.0, installed 18.3.1
167
+
168
+ Unused shared (safe to remove):
169
+ ✗ lodash — 0 imports, shared without singleton
170
+ ✗ @tanstack/react-query — 0 imports, shared as singleton
171
+
172
+ Candidates (consider adding to shared):
173
+ → mobx (12 imports in 8 files, via re-export in src/shared/index.ts)
174
+ → react-router-dom (6 imports in 4 files)
175
+
176
+ Singleton risks (add singleton: true):
177
+ ⚠ react-router-dom — manages global state, singleton: true recommended
178
+
179
+ Eager risks (add singleton: true or remove eager: true):
180
+ ⚠ react-dom — eager: true without singleton: true, risk of duplicate instances
181
+
182
+ Total: 12 shared, 10 used, 2 unused, 2 candidates, 1 mismatch, 1 eager risks
183
+ ```
184
+
185
+ ## CI pipeline: project → federation
186
+
187
+ Each microfrontend generates a manifest as a build artifact:
188
+
189
+ ```yaml
190
+ jobs:
191
+ build-checkout:
192
+ steps:
193
+ - run: npm run build # MfSharedInspectorPlugin writes project-manifest.json
194
+ - uses: actions/upload-artifact@v4
195
+ with:
196
+ name: manifest-checkout
197
+ path: project-manifest.json
198
+ ```
199
+
200
+ `analyzeFederation()` (v0.2) will accept N manifests and detect cross-MF issues: ghost sharing, host gaps, version conflicts.
201
+
202
+ ## API reference
203
+
204
+ ### `buildProjectManifest(options)`
205
+
206
+ | Option | Type | Default | Description |
207
+ |--------|------|---------|-------------|
208
+ | `name` | `string` | — | Project name |
209
+ | `sourceDirs` | `string[]` | — | Directories to scan |
210
+ | `depth` | `'direct' \| 'local-graph'` | `'local-graph'` | Scan depth |
211
+ | `sharedConfig` | `Record<string, SharedDepConfig>` | `{}` | MF shared config |
212
+ | `packageJsonPath` | `string` | `'./package.json'` | Path to package.json |
213
+ | `extensions` | `string[]` | `['.ts','.tsx','.js','.jsx']` | File extensions |
214
+ | `ignore` | `string[]` | `[]` | Packages to exclude (supports `@scope/*`) |
215
+ | `tsconfigPath` | `string` | `undefined` | tsconfig.json for path alias resolution |
216
+ | `workspacePackages` | `string[]` | `[]` | Local monorepo packages to exclude |
217
+ | `kind` | `'host' \| 'remote' \| 'unknown'` | `'unknown'` | Project role |
218
+
219
+ ### `analyzeProject(manifest, options?)`
220
+
221
+ | Option | Type | Default | Description |
222
+ |--------|------|---------|-------------|
223
+ | `alwaysShared` | `string[]` | `['react','react-dom']` | Never flag as unused |
224
+ | `additionalCandidates` | `string[]` | `[]` | Extend built-in candidates list |
225
+ | `additionalSingletonRisks` | `string[]` | `[]` | Extend built-in singleton-risk list |
226
+
227
+ ## Detection categories
228
+
229
+ | Category | Type | Description |
230
+ |----------|------|-------------|
231
+ | `mismatched` | Deterministic | `requiredVersion` doesn't satisfy installed version |
232
+ | `unused` | Deterministic* | In `shared` config but not observed in scanned sources |
233
+ | `candidates` | Heuristic | Observed packages not in `shared` that are typically shared |
234
+ | `singletonRisks` | Heuristic | Global-state packages shared without `singleton: true` |
235
+ | `eagerRisks` | Heuristic | `eager: true` without `singleton: true` — risk of duplicate instances |
236
+
237
+ *Within the visibility of the chosen depth.*
238
+
239
+ ## Known limitations
240
+
241
+ - **TypeScript path aliases without `tsconfigPath`**: aliased imports are treated as external package names. Pass `tsconfigPath` to resolve them correctly.
242
+ - **Dynamic imports with variables** (`import(moduleName)`): not analysed — requires runtime information.
243
+ - **Exact tsconfig alias patterns** (non-wildcard, e.g. `"@root": ["."]`): not supported, only `"@alias/*"` wildcard form.
244
+ - **`analyzeFederation()`** (cross-MF analysis): v0.2.
245
+
246
+ ## Demo
247
+
248
+ ```bash
249
+ npx tsx packages/shared-inspector/demo/run.ts
250
+ ```
251
+
252
+ ## License
253
+
254
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { ProjectManifest, ProjectReport, AnalysisOptions } from '../types.js';
2
+ /**
3
+ * Analyze a single ProjectManifest and return a ProjectReport.
4
+ *
5
+ * Pure function — no I/O. All findings are scoped to what the collector
6
+ * observed at the chosen depth (manifest.source.depth).
7
+ */
8
+ export declare function analyzeProject(manifest: ProjectManifest, options?: AnalysisOptions): ProjectReport;
9
+ //# sourceMappingURL=analyze-project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-project.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze-project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInF;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,eAAe,GACxB,aAAa,CAiCf"}
@@ -0,0 +1,38 @@
1
+ import { detectIssues } from './detect-issues.js';
2
+ import { mergePolicy } from './policy.js';
3
+ /**
4
+ * Analyze a single ProjectManifest and return a ProjectReport.
5
+ *
6
+ * Pure function — no I/O. All findings are scoped to what the collector
7
+ * observed at the chosen depth (manifest.source.depth).
8
+ */
9
+ export function analyzeProject(manifest, options) {
10
+ const policy = mergePolicy(options);
11
+ const { unused, candidates, mismatched, singletonRisks, eagerRisks } = detectIssues({
12
+ resolvedPackages: manifest.usage.resolvedPackages,
13
+ packageDetails: manifest.usage.packageDetails,
14
+ sharedDeclared: manifest.shared.declared,
15
+ installedVersions: manifest.versions.installed,
16
+ policy,
17
+ });
18
+ const totalShared = Object.keys(manifest.shared.declared).length;
19
+ const resolvedSet = new Set(manifest.usage.resolvedPackages);
20
+ const usedShared = Object.keys(manifest.shared.declared).filter(pkg => resolvedSet.has(pkg)).length;
21
+ return {
22
+ unused,
23
+ candidates,
24
+ mismatched,
25
+ singletonRisks,
26
+ eagerRisks,
27
+ summary: {
28
+ totalShared,
29
+ usedShared,
30
+ unusedCount: unused.length,
31
+ candidatesCount: candidates.length,
32
+ mismatchedCount: mismatched.length,
33
+ singletonRisksCount: singletonRisks.length,
34
+ eagerRisksCount: eagerRisks.length,
35
+ },
36
+ };
37
+ }
38
+ //# sourceMappingURL=analyze-project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-project.js","sourceRoot":"","sources":["../../src/analyzer/analyze-project.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAyB,EACzB,OAAyB;IAEzB,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAEpC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC;QAClF,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,gBAAgB;QACjD,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,cAAc;QAC7C,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ;QACxC,iBAAiB,EAAE,QAAQ,CAAC,QAAQ,CAAC,SAAS;QAC9C,MAAM;KACP,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IACjE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACpE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CACrB,CAAC,MAAM,CAAC;IAET,OAAO;QACL,MAAM;QACN,UAAU;QACV,UAAU;QACV,cAAc;QACd,UAAU;QACV,OAAO,EAAE;YACP,WAAW;YACX,UAAU;YACV,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,eAAe,EAAE,UAAU,CAAC,MAAM;YAClC,eAAe,EAAE,UAAU,CAAC,MAAM;YAClC,mBAAmB,EAAE,cAAc,CAAC,MAAM;YAC1C,eAAe,EAAE,UAAU,CAAC,MAAM;SACnC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { UnusedEntry, CandidateEntry, MismatchedEntry, SingletonRiskEntry, EagerRiskEntry } from '../types.js';
2
+ import type { ResolvedPolicy } from './policy.js';
3
+ export interface DetectIssuesInput {
4
+ resolvedPackages: string[];
5
+ packageDetails: Array<{
6
+ package: string;
7
+ importCount: number;
8
+ files: string[];
9
+ via: 'direct' | 'reexport';
10
+ }>;
11
+ sharedDeclared: Record<string, {
12
+ singleton?: boolean;
13
+ eager?: boolean;
14
+ requiredVersion?: string;
15
+ }>;
16
+ /** From node_modules. Empty object = not accessible, mismatch checks skipped for absent entries. */
17
+ installedVersions: Record<string, string>;
18
+ policy: ResolvedPolicy;
19
+ }
20
+ export interface DetectIssuesResult {
21
+ unused: UnusedEntry[];
22
+ candidates: CandidateEntry[];
23
+ mismatched: MismatchedEntry[];
24
+ singletonRisks: SingletonRiskEntry[];
25
+ eagerRisks: EagerRiskEntry[];
26
+ }
27
+ /**
28
+ * Pure function — no I/O.
29
+ * Cross-checks manifest data against policy to produce findings.
30
+ * All results are scoped to what the collector observed at the chosen depth.
31
+ */
32
+ export declare function detectIssues(input: DetectIssuesInput): DetectIssuesResult;
33
+ //# sourceMappingURL=detect-issues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-issues.d.ts","sourceRoot":"","sources":["../../src/analyzer/detect-issues.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACf,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,KAAK,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC5B,CAAC,CAAC;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE;QAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC,CAAC;IACH,oGAAoG;IACpG,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,cAAc,EAAE,kBAAkB,EAAE,CAAC;IACrC,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CA8DzE"}
@@ -0,0 +1,69 @@
1
+ import semver from 'semver';
2
+ // ─── Core detection ───────────────────────────────────────────────────────────
3
+ /**
4
+ * Pure function — no I/O.
5
+ * Cross-checks manifest data against policy to produce findings.
6
+ * All results are scoped to what the collector observed at the chosen depth.
7
+ */
8
+ export function detectIssues(input) {
9
+ const resolvedSet = new Set(input.resolvedPackages);
10
+ const sharedEntries = Object.entries(input.sharedDeclared);
11
+ const detailsMap = new Map(input.packageDetails.map(d => [d.package, d]));
12
+ // Unused: in shared config but not observed in resolvedPackages
13
+ // Packages in alwaysShared are always excluded from this list.
14
+ const unused = sharedEntries
15
+ .filter(([pkg]) => !resolvedSet.has(pkg) && !input.policy.alwaysShared.has(pkg))
16
+ .map(([pkg, config]) => ({
17
+ package: pkg,
18
+ singleton: config.singleton ?? false,
19
+ }));
20
+ // Candidates: observed but not shared, and in the built-in share-candidates list
21
+ const candidates = input.resolvedPackages
22
+ .filter(pkg => !input.sharedDeclared[pkg] && input.policy.shareCandidates.has(pkg))
23
+ .map(pkg => {
24
+ const detail = detailsMap.get(pkg);
25
+ return {
26
+ package: pkg,
27
+ importCount: detail?.importCount ?? 1,
28
+ files: detail?.files ?? [],
29
+ via: detail?.via ?? 'direct',
30
+ };
31
+ });
32
+ // Mismatched: requiredVersion does not satisfy the installed version.
33
+ // Skipped when installed version is unknown (installedVersions[pkg] absent).
34
+ const mismatched = [];
35
+ for (const [pkg, config] of sharedEntries) {
36
+ if (!config.requiredVersion)
37
+ continue;
38
+ const installed = input.installedVersions[pkg];
39
+ if (!installed)
40
+ continue;
41
+ // semver.validRange returns null for invalid ranges; skip to avoid false positives
42
+ if (!semver.validRange(config.requiredVersion))
43
+ continue;
44
+ try {
45
+ if (!semver.satisfies(installed, config.requiredVersion)) {
46
+ mismatched.push({
47
+ package: pkg,
48
+ configured: config.requiredVersion,
49
+ installed,
50
+ });
51
+ }
52
+ }
53
+ catch {
54
+ // Defensive: skip if satisfies throws unexpectedly
55
+ }
56
+ }
57
+ // Singleton risks: in the singleton-risk list but shared without singleton: true
58
+ const singletonRisks = sharedEntries
59
+ .filter(([pkg, config]) => input.policy.singletonRisks.has(pkg) && !config.singleton)
60
+ .map(([pkg]) => ({ package: pkg }));
61
+ // Eager risks: eager: true without singleton: true.
62
+ // Eager-loading without singleton can produce duplicate module instances
63
+ // when multiple MFs initialise the same package before version negotiation.
64
+ const eagerRisks = sharedEntries
65
+ .filter(([_, config]) => config.eager === true && config.singleton !== true)
66
+ .map(([pkg]) => ({ package: pkg }));
67
+ return { unused, candidates, mismatched, singletonRisks, eagerRisks };
68
+ }
69
+ //# sourceMappingURL=detect-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-issues.js","sourceRoot":"","sources":["../../src/analyzer/detect-issues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { AnalysisOptions } from '../types.js';
2
+ /**
3
+ * Packages that are never flagged as unused by default.
4
+ * React is consumed transitively by JSX Transform (no explicit import needed).
5
+ */
6
+ export declare const DEFAULT_ALWAYS_SHARED: readonly string[];
7
+ /**
8
+ * Packages with global state — should be shared with singleton: true.
9
+ * Duplicating these causes runtime errors (e.g. "Invalid hook call").
10
+ */
11
+ export declare const SINGLETON_RISK_PACKAGES: readonly string[];
12
+ /**
13
+ * Packages that are typically shared across microfrontends.
14
+ * Used for the candidates heuristic — presence in this list + not in shared config = suggestion.
15
+ */
16
+ export declare const SHARE_CANDIDATE_PACKAGES: readonly string[];
17
+ export interface ResolvedPolicy {
18
+ alwaysShared: Set<string>;
19
+ singletonRisks: Set<string>;
20
+ shareCandidates: Set<string>;
21
+ }
22
+ /**
23
+ * Merge built-in policy with user-supplied AnalysisOptions.
24
+ * User lists extend (not replace) the built-in lists.
25
+ */
26
+ export declare function mergePolicy(options?: AnalysisOptions): ResolvedPolicy;
27
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/analyzer/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAGlD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAepD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EA2BrD,CAAC;AAIF,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,CAgBrE"}
@@ -0,0 +1,84 @@
1
+ // ─── Built-in lists ───────────────────────────────────────────────────────────
2
+ /**
3
+ * Packages that are never flagged as unused by default.
4
+ * React is consumed transitively by JSX Transform (no explicit import needed).
5
+ */
6
+ export const DEFAULT_ALWAYS_SHARED = [
7
+ 'react',
8
+ 'react-dom',
9
+ ];
10
+ /**
11
+ * Packages with global state — should be shared with singleton: true.
12
+ * Duplicating these causes runtime errors (e.g. "Invalid hook call").
13
+ */
14
+ export const SINGLETON_RISK_PACKAGES = [
15
+ 'react',
16
+ 'react-dom',
17
+ 'react-router',
18
+ 'react-router-dom',
19
+ 'vue',
20
+ 'vue-router',
21
+ 'mobx',
22
+ 'mobx-react',
23
+ 'mobx-react-lite',
24
+ 'styled-components',
25
+ '@emotion/react',
26
+ '@emotion/styled',
27
+ 'redux',
28
+ '@reduxjs/toolkit',
29
+ ];
30
+ /**
31
+ * Packages that are typically shared across microfrontends.
32
+ * Used for the candidates heuristic — presence in this list + not in shared config = suggestion.
33
+ */
34
+ export const SHARE_CANDIDATE_PACKAGES = [
35
+ // Frameworks
36
+ 'react',
37
+ 'react-dom',
38
+ 'vue',
39
+ 'svelte',
40
+ 'solid-js',
41
+ // Routing
42
+ 'react-router',
43
+ 'react-router-dom',
44
+ 'vue-router',
45
+ // State management
46
+ 'mobx',
47
+ 'mobx-react',
48
+ 'mobx-react-lite',
49
+ 'redux',
50
+ '@reduxjs/toolkit',
51
+ 'zustand',
52
+ 'jotai',
53
+ 'recoil',
54
+ // Data fetching
55
+ '@tanstack/react-query',
56
+ 'swr',
57
+ // Styling
58
+ 'styled-components',
59
+ '@emotion/react',
60
+ '@emotion/styled',
61
+ ];
62
+ /**
63
+ * Merge built-in policy with user-supplied AnalysisOptions.
64
+ * User lists extend (not replace) the built-in lists.
65
+ */
66
+ export function mergePolicy(options) {
67
+ const alwaysShared = new Set(DEFAULT_ALWAYS_SHARED);
68
+ const singletonRisks = new Set(SINGLETON_RISK_PACKAGES);
69
+ const shareCandidates = new Set(SHARE_CANDIDATE_PACKAGES);
70
+ if (options?.alwaysShared) {
71
+ for (const pkg of options.alwaysShared)
72
+ alwaysShared.add(pkg);
73
+ }
74
+ if (options?.additionalSingletonRisks) {
75
+ for (const pkg of options.additionalSingletonRisks)
76
+ singletonRisks.add(pkg);
77
+ }
78
+ if (options?.additionalCandidates) {
79
+ for (const pkg of options.additionalCandidates)
80
+ shareCandidates.add(pkg);
81
+ }
82
+ return { alwaysShared, singletonRisks, shareCandidates };
83
+ }
84
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/analyzer/policy.ts"],"names":[],"mappings":"AAEA,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,OAAO;IACP,WAAW;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,OAAO;IACP,WAAW;IACX,cAAc;IACd,kBAAkB;IAClB,KAAK;IACL,YAAY;IACZ,MAAM;IACN,YAAY;IACZ,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;IAChB,iBAAiB;IACjB,OAAO;IACP,kBAAkB;CACnB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,aAAa;IACb,OAAO;IACP,WAAW;IACX,KAAK;IACL,QAAQ;IACR,UAAU;IACV,UAAU;IACV,cAAc;IACd,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,MAAM;IACN,YAAY;IACZ,iBAAiB;IACjB,OAAO;IACP,kBAAkB;IAClB,SAAS;IACT,OAAO;IACP,QAAQ;IACR,gBAAgB;IAChB,uBAAuB;IACvB,KAAK;IACL,UAAU;IACV,mBAAmB;IACnB,gBAAgB;IAChB,iBAAiB;CAClB,CAAC;AAUF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,qBAAqB,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,uBAAuB,CAAC,CAAC;IAChE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,CAAC;IAElE,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;QAC1B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,YAAY;YAAE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,EAAE,wBAAwB,EAAE,CAAC;QACtC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,wBAAwB;YAAE,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,OAAO,EAAE,oBAAoB,EAAE,CAAC;QAClC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,oBAAoB;YAAE,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CollectorOptions, ProjectManifest } from '../types.js';
2
+ /**
3
+ * Phase 1 of the two-phase pipeline: collect observable dependency facts
4
+ * and assemble them into a self-contained ProjectManifest.
5
+ *
6
+ * The manifest captures facts at the chosen depth; it does not make
7
+ * any policy decisions — that is the analyzer's job.
8
+ */
9
+ export declare function buildProjectManifest(options: CollectorOptions): Promise<ProjectManifest>;
10
+ //# sourceMappingURL=build-project-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-project-manifest.d.ts","sourceRoot":"","sources":["../../src/collector/build-project-manifest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAC;AAQxF;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CA2F1B"}
@@ -0,0 +1,82 @@
1
+ import { resolve, dirname } from 'node:path';
2
+ import { collectImports, scanFiles } from './collect-imports.js';
3
+ import { traverseLocalModules } from './traverse-local-modules.js';
4
+ import { parseSharedConfig } from './parse-shared-config.js';
5
+ import { resolveVersions } from './resolve-versions.js';
6
+ const DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
7
+ /**
8
+ * Phase 1 of the two-phase pipeline: collect observable dependency facts
9
+ * and assemble them into a self-contained ProjectManifest.
10
+ *
11
+ * The manifest captures facts at the chosen depth; it does not make
12
+ * any policy decisions — that is the analyzer's job.
13
+ */
14
+ export async function buildProjectManifest(options) {
15
+ const { name, sourceDirs, depth = 'local-graph', sharedConfig, kind = 'unknown', packageJsonPath = './package.json', extensions = DEFAULT_EXTENSIONS, ignore, tsconfigPath, workspacePackages, } = options;
16
+ const resolvedPkgJsonPath = resolve(packageJsonPath);
17
+ const root = dirname(resolvedPkgJsonPath);
18
+ // ── Step 1: scan files (for filesScanned count) ───────────────────────────
19
+ const allFiles = await scanFiles(sourceDirs, extensions);
20
+ // ── Step 2: collect package occurrences ───────────────────────────────────
21
+ let occurrences;
22
+ let effectiveDepth;
23
+ if (depth === 'local-graph') {
24
+ occurrences = await traverseLocalModules({ sourceDirs, extensions, ignore, tsconfigPath, workspacePackages });
25
+ effectiveDepth = 'local-graph';
26
+ }
27
+ else {
28
+ occurrences = await collectImports({ sourceDirs, extensions, ignore, workspacePackages });
29
+ effectiveDepth = 'direct';
30
+ }
31
+ // ── Step 3: aggregate occurrences into packageDetails ────────────────────
32
+ const byPackage = new Map();
33
+ for (const occ of occurrences) {
34
+ if (!byPackage.has(occ.package)) {
35
+ byPackage.set(occ.package, { files: new Set([occ.file]), via: occ.via });
36
+ }
37
+ else {
38
+ const entry = byPackage.get(occ.package);
39
+ entry.files.add(occ.file);
40
+ // Direct import takes precedence over reexport at the manifest level too
41
+ if (occ.via === 'direct')
42
+ entry.via = 'direct';
43
+ }
44
+ }
45
+ const packageDetails = Array.from(byPackage.entries()).map(([pkg, { files, via }]) => ({
46
+ package: pkg,
47
+ importCount: files.size,
48
+ files: [...files].sort(),
49
+ via,
50
+ }));
51
+ const directPackages = packageDetails
52
+ .filter((d) => d.via === 'direct')
53
+ .map((d) => d.package);
54
+ // resolvedPackages = all observed packages (direct + reexport)
55
+ const resolvedPackages = packageDetails.map((d) => d.package);
56
+ // ── Step 4: parse shared config ───────────────────────────────────────────
57
+ const sharedDeclared = parseSharedConfig(sharedConfig);
58
+ // ── Step 5: resolve versions ──────────────────────────────────────────────
59
+ const versions = await resolveVersions(resolvedPkgJsonPath);
60
+ // ── Assemble manifest ─────────────────────────────────────────────────────
61
+ return {
62
+ schemaVersion: 1,
63
+ generatedAt: new Date().toISOString(),
64
+ project: { name, root, kind },
65
+ source: {
66
+ depth: effectiveDepth,
67
+ sourceDirs,
68
+ filesScanned: allFiles.length,
69
+ },
70
+ usage: {
71
+ directPackages,
72
+ resolvedPackages,
73
+ packageDetails,
74
+ },
75
+ shared: {
76
+ declared: sharedDeclared,
77
+ source: 'explicit',
78
+ },
79
+ versions,
80
+ };
81
+ }
82
+ //# sourceMappingURL=build-project-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-project-manifest.js","sourceRoot":"","sources":["../../src/collector/build-project-manifest.ts"],"names":[],"mappings":"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"}