@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.
- package/README.md +195 -127
- 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 +5 -2
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// ─── Diagnostics knowledge base ──────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Maps (package, issue-kind) → { risk description, fix snippet hint }.
|
|
4
|
+
// Used by formatReport / formatFederationReport to produce actionable output.
|
|
5
|
+
// ─── Per-package knowledge ────────────────────────────────────────────────────
|
|
6
|
+
const PACKAGE_DIAGNOSTICS = {
|
|
7
|
+
react: {
|
|
8
|
+
mismatch: 'Invalid hook call, broken context across MFs',
|
|
9
|
+
candidate: 'Multiple React copies → invalid hook call',
|
|
10
|
+
'singleton-risk': 'Multiple React instances → invalid hook call, broken context',
|
|
11
|
+
'eager-risk': 'Duplicate React before negotiation completes → invalid hook call',
|
|
12
|
+
unused: 'React is consumed by JSX transform — removing may break JSX in remotes',
|
|
13
|
+
},
|
|
14
|
+
'react-dom': {
|
|
15
|
+
mismatch: 'Rendering broken — host and remote use different ReactDOM',
|
|
16
|
+
candidate: 'Multiple ReactDOM copies → broken rendering',
|
|
17
|
+
'singleton-risk': 'Multiple ReactDOM instances → broken rendering, portals fail',
|
|
18
|
+
unused: 'ReactDOM is consumed implicitly — removing may break renders in remotes',
|
|
19
|
+
},
|
|
20
|
+
'react-router': {
|
|
21
|
+
mismatch: 'Router version mismatch → broken navigation, hooks (useParams, useNavigate) broken',
|
|
22
|
+
candidate: 'Each MF has its own router instance → history state conflicts',
|
|
23
|
+
'singleton-risk': 'Multiple router instances → navigation conflicts between MFs',
|
|
24
|
+
unused: 'Route declarations in the remote may silently stop working',
|
|
25
|
+
},
|
|
26
|
+
'react-router-dom': {
|
|
27
|
+
mismatch: 'Router version mismatch → broken navigation',
|
|
28
|
+
candidate: 'Each MF has its own router → useNavigate / useParams broken cross-MF',
|
|
29
|
+
'singleton-risk': 'Multiple router instances → useNavigate / useParams broken',
|
|
30
|
+
unused: 'Link and Route components may stop working in other MFs',
|
|
31
|
+
},
|
|
32
|
+
mobx: {
|
|
33
|
+
mismatch: 'Observable state won\'t sync — MFs run different MobX versions',
|
|
34
|
+
candidate: 'Each MF gets its own MobX — observables and reactions won\'t sync between MFs',
|
|
35
|
+
'singleton-risk': 'Multiple MobX instances → cross-MF reactions won\'t fire, state isolated',
|
|
36
|
+
unused: 'MobX stores in the remote will be untracked',
|
|
37
|
+
},
|
|
38
|
+
'mobx-react': {
|
|
39
|
+
mismatch: 'observer() wraps components from different MobX instances',
|
|
40
|
+
candidate: 'Multiple mobx-react copies → observer() connects to wrong store instance',
|
|
41
|
+
'singleton-risk': 'Multiple mobx-react instances → observer() binds to isolated store',
|
|
42
|
+
},
|
|
43
|
+
'mobx-react-lite': {
|
|
44
|
+
mismatch: 'useObserver connects to wrong MobX instance',
|
|
45
|
+
candidate: 'Each MF gets its own observer binding',
|
|
46
|
+
'singleton-risk': 'Multiple mobx-react-lite → useObserver connects to wrong store',
|
|
47
|
+
},
|
|
48
|
+
redux: {
|
|
49
|
+
mismatch: 'Redux version mismatch → dispatch / subscribe API incompatible',
|
|
50
|
+
candidate: 'Each MF creates its own store — actions won\'t propagate across MFs',
|
|
51
|
+
'singleton-risk': 'Multiple Redux stores → dispatches won\'t propagate cross-MF',
|
|
52
|
+
},
|
|
53
|
+
'@reduxjs/toolkit': {
|
|
54
|
+
mismatch: 'RTK version mismatch → createSlice / createAsyncThunk API differences',
|
|
55
|
+
candidate: 'Each MF bundles its own RTK — separate store instances, no shared state',
|
|
56
|
+
'singleton-risk': 'Multiple RTK instances → slices belong to different stores',
|
|
57
|
+
},
|
|
58
|
+
zustand: {
|
|
59
|
+
mismatch: 'Zustand API mismatch → store subscriptions may not work correctly',
|
|
60
|
+
candidate: 'Each MF gets its own Zustand store — state not shared across MFs',
|
|
61
|
+
'singleton-risk': 'Multiple Zustand instances → store subscriptions not shared',
|
|
62
|
+
},
|
|
63
|
+
jotai: {
|
|
64
|
+
candidate: 'Each MF gets its own Jotai atom scope — atoms won\'t sync',
|
|
65
|
+
'singleton-risk': 'Multiple Jotai instances → atoms not shared between MFs',
|
|
66
|
+
},
|
|
67
|
+
recoil: {
|
|
68
|
+
candidate: 'Each MF gets its own Recoil root — atoms isolated per MF',
|
|
69
|
+
'singleton-risk': 'Multiple Recoil instances → RecoilRoot state not shared',
|
|
70
|
+
},
|
|
71
|
+
vue: {
|
|
72
|
+
mismatch: 'Vue runtime mismatch → app.use() and composables broken',
|
|
73
|
+
candidate: 'Each MF bundles Vue — composables won\'t share reactive state',
|
|
74
|
+
'singleton-risk': 'Multiple Vue runtimes → provide/inject across MFs broken',
|
|
75
|
+
},
|
|
76
|
+
'vue-router': {
|
|
77
|
+
mismatch: 'Router version mismatch → navigation guards and hooks broken',
|
|
78
|
+
candidate: 'Each MF has its own Vue Router instance → navigation conflicts',
|
|
79
|
+
'singleton-risk': 'Multiple router instances → useRoute / useRouter broken cross-MF',
|
|
80
|
+
},
|
|
81
|
+
'styled-components': {
|
|
82
|
+
mismatch: 'Style injection broken — className collisions between MFs',
|
|
83
|
+
candidate: 'Each MF bundles its own styled-components — class name conflicts in SSR',
|
|
84
|
+
'singleton-risk': 'Multiple styled-components → theme context not shared, class collisions',
|
|
85
|
+
},
|
|
86
|
+
'@emotion/react': {
|
|
87
|
+
mismatch: 'Emotion cache mismatch → styles not injected correctly across MFs',
|
|
88
|
+
candidate: 'Each MF has its own Emotion cache — styles may conflict or duplicate',
|
|
89
|
+
'singleton-risk': 'Multiple Emotion instances → theme context not shared, styles broken',
|
|
90
|
+
},
|
|
91
|
+
'@emotion/styled': {
|
|
92
|
+
mismatch: 'Emotion version mismatch → styled components lose theme context',
|
|
93
|
+
candidate: 'Each MF bundles @emotion/styled independently',
|
|
94
|
+
'singleton-risk': 'Multiple Emotion instances → styled components lose theme context',
|
|
95
|
+
},
|
|
96
|
+
'@tanstack/react-query': {
|
|
97
|
+
candidate: 'Each MF has its own QueryClient → no shared cache, duplicate requests',
|
|
98
|
+
'singleton-risk': 'Multiple QueryClient instances → cache not shared between MFs',
|
|
99
|
+
},
|
|
100
|
+
swr: {
|
|
101
|
+
candidate: 'Each MF has its own SWR cache → duplicate fetches, no deduplication',
|
|
102
|
+
'singleton-risk': 'Multiple SWR instances → cache not shared between MFs',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
// ─── Generic fallbacks ────────────────────────────────────────────────────────
|
|
106
|
+
const GENERIC_RISK = {
|
|
107
|
+
mismatch: 'Version mismatch may cause runtime errors or silent incompatibilities',
|
|
108
|
+
unused: 'Wastes bundle negotiation overhead with no import benefit',
|
|
109
|
+
candidate: 'Each MF bundles its own copy — larger bundles, possible state desync',
|
|
110
|
+
'singleton-risk': 'Duplicate instances may cause unexpected runtime behavior',
|
|
111
|
+
'eager-risk': 'Eager loading without singleton risks duplicate instances during negotiation',
|
|
112
|
+
};
|
|
113
|
+
export function getDiagnostic(pkg, kind) {
|
|
114
|
+
const risk = PACKAGE_DIAGNOSTICS[pkg]?.[kind] ?? GENERIC_RISK[kind];
|
|
115
|
+
return { risk };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Returns a ready-to-paste webpack/rspack shared config snippet.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* buildFixSnippet('react', { singleton: true, requiredVersion: '^18.2.0' })
|
|
122
|
+
* // →
|
|
123
|
+
* // shared: {
|
|
124
|
+
* // react: { singleton: true, requiredVersion: "^18.2.0" }
|
|
125
|
+
* // }
|
|
126
|
+
*/
|
|
127
|
+
export function buildFixSnippet(pkg, cfg = {}) {
|
|
128
|
+
const entries = [];
|
|
129
|
+
if (cfg.singleton)
|
|
130
|
+
entries.push('singleton: true');
|
|
131
|
+
if (cfg.requiredVersion)
|
|
132
|
+
entries.push(`requiredVersion: "${cfg.requiredVersion}"`);
|
|
133
|
+
if (cfg.eager)
|
|
134
|
+
entries.push('eager: true');
|
|
135
|
+
const inner = entries.length > 0 ? ` { ${entries.join(', ')} }` : ' {}';
|
|
136
|
+
return `shared: {\n ${pkg}:${inner}\n}`;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/reporter/diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import type { FederationReport } from '../types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Formats a FederationReport as
|
|
3
|
+
* Formats a FederationReport as actionable diagnostic cards.
|
|
4
|
+
*
|
|
5
|
+
* Each finding shows:
|
|
6
|
+
* - Issue title + package name
|
|
7
|
+
* - Which MFs are involved
|
|
8
|
+
* - Risk description (what breaks at runtime)
|
|
9
|
+
* - Ready-to-paste fix snippet
|
|
4
10
|
*
|
|
5
11
|
* Section order (by severity):
|
|
6
|
-
* 1. Version conflicts —
|
|
7
|
-
* 2. Singleton mismatches —
|
|
8
|
-
* 3. Host gaps —
|
|
9
|
-
* 4. Ghost shares —
|
|
12
|
+
* 1. Version conflicts — most dangerous, singleton negotiation fails silently
|
|
13
|
+
* 2. Singleton mismatches — runtime risk, unpredictable instance selection
|
|
14
|
+
* 3. Host gaps — bundle duplication, no shared state
|
|
15
|
+
* 4. Ghost shares — dead coupling, safe to remove
|
|
10
16
|
* 5. Summary line
|
|
11
17
|
*/
|
|
12
18
|
export declare function formatFederationReport(report: FederationReport): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-federation-report.d.ts","sourceRoot":"","sources":["../../src/reporter/format-federation-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"format-federation-report.d.ts","sourceRoot":"","sources":["../../src/reporter/format-federation-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAM1E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAiGvE"}
|
|
@@ -1,72 +1,97 @@
|
|
|
1
|
+
import { getDiagnostic, buildFixSnippet } from './diagnostics.js';
|
|
2
|
+
import { scoreFederationReport, formatScoreBlock } from './scoring.js';
|
|
3
|
+
const HR = '─'.repeat(60);
|
|
1
4
|
/**
|
|
2
|
-
* Formats a FederationReport as
|
|
5
|
+
* Formats a FederationReport as actionable diagnostic cards.
|
|
6
|
+
*
|
|
7
|
+
* Each finding shows:
|
|
8
|
+
* - Issue title + package name
|
|
9
|
+
* - Which MFs are involved
|
|
10
|
+
* - Risk description (what breaks at runtime)
|
|
11
|
+
* - Ready-to-paste fix snippet
|
|
3
12
|
*
|
|
4
13
|
* Section order (by severity):
|
|
5
|
-
* 1. Version conflicts —
|
|
6
|
-
* 2. Singleton mismatches —
|
|
7
|
-
* 3. Host gaps —
|
|
8
|
-
* 4. Ghost shares —
|
|
14
|
+
* 1. Version conflicts — most dangerous, singleton negotiation fails silently
|
|
15
|
+
* 2. Singleton mismatches — runtime risk, unpredictable instance selection
|
|
16
|
+
* 3. Host gaps — bundle duplication, no shared state
|
|
17
|
+
* 4. Ghost shares — dead coupling, safe to remove
|
|
9
18
|
* 5. Summary line
|
|
10
19
|
*/
|
|
11
20
|
export function formatFederationReport(report) {
|
|
12
21
|
const lines = [];
|
|
13
22
|
const { ghostShares, hostGaps, versionConflicts, singletonMismatches, summary } = report;
|
|
14
|
-
|
|
15
|
-
lines.push('', `[MfSharedInspector] federation analysis (${summary.totalManifests} MFs)`, '');
|
|
23
|
+
lines.push('', `[MfSharedInspector] federation analysis (${summary.totalManifests} MFs)`, HR, '');
|
|
16
24
|
const hasFindings = versionConflicts.length > 0 ||
|
|
17
25
|
singletonMismatches.length > 0 ||
|
|
18
26
|
hostGaps.length > 0 ||
|
|
19
27
|
ghostShares.length > 0;
|
|
20
28
|
if (!hasFindings) {
|
|
21
|
-
lines.push(' No federation-level issues found.');
|
|
29
|
+
lines.push(' ✓ No federation-level issues found. Everything looks consistent.');
|
|
30
|
+
lines.push('');
|
|
31
|
+
lines.push(HR);
|
|
32
|
+
lines.push(formatScoreBlock(scoreFederationReport(report), 'version conflicts', 'singleton mismatches, host gaps', 'ghost shares'));
|
|
22
33
|
lines.push('');
|
|
23
|
-
lines.push(`
|
|
34
|
+
lines.push(`Total: ${summary.totalManifests} MFs analysed, 0 issues.`);
|
|
24
35
|
lines.push('');
|
|
25
36
|
return lines.join('\n');
|
|
26
37
|
}
|
|
27
38
|
// ── 1. Version conflicts ───────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
for (const c of versionConflicts) {
|
|
40
|
+
const { risk } = getDiagnostic(c.package, 'mismatch');
|
|
41
|
+
lines.push(`⚠ Version Conflict — ${c.package}`);
|
|
42
|
+
for (const [mf, v] of Object.entries(c.versions)) {
|
|
43
|
+
lines.push(` ${mf}: ${v}`);
|
|
44
|
+
}
|
|
45
|
+
lines.push(` → Risk: MF singleton negotiation will silently load wrong version → ${risk}`);
|
|
46
|
+
lines.push(` 💡 Fix: Align requiredVersion across all MFs:`);
|
|
47
|
+
const best = pickHighestRequiredVersion(c);
|
|
48
|
+
for (const l of buildFixSnippet(c.package, { singleton: true, requiredVersion: best }).split('\n')) {
|
|
49
|
+
lines.push(` ${l}`);
|
|
35
50
|
}
|
|
36
51
|
lines.push('');
|
|
37
52
|
}
|
|
38
53
|
// ── 2. Singleton mismatches ────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
54
|
+
for (const s of singletonMismatches) {
|
|
55
|
+
const { risk } = getDiagnostic(s.package, 'singleton-risk');
|
|
56
|
+
lines.push(`⚠ Singleton Mismatch — ${s.package}`);
|
|
57
|
+
lines.push(` singleton in: [${s.singletonIn.join(', ')}]`);
|
|
58
|
+
lines.push(` not singleton in: [${s.nonSingletonIn.join(', ')}]`);
|
|
59
|
+
lines.push(` → Risk: ${risk}`);
|
|
60
|
+
lines.push(` 💡 Fix: Add singleton: true to all MFs that share ${s.package}`);
|
|
44
61
|
lines.push('');
|
|
45
62
|
}
|
|
46
63
|
// ── 3. Host gaps ───────────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
for (const g of hostGaps) {
|
|
65
|
+
const { risk } = getDiagnostic(g.package, 'candidate');
|
|
66
|
+
lines.push(`→ Host Gap — ${g.package}`);
|
|
67
|
+
lines.push(` used by: [${g.missingIn.join(', ')}], not in shared config`);
|
|
68
|
+
lines.push(` → Risk: ${risk}`);
|
|
69
|
+
lines.push(` 💡 Fix:`);
|
|
70
|
+
for (const l of buildFixSnippet(g.package, { singleton: true }).split('\n')) {
|
|
71
|
+
lines.push(` ${l}`);
|
|
51
72
|
}
|
|
52
73
|
lines.push('');
|
|
53
74
|
}
|
|
54
75
|
// ── 4. Ghost shares ────────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
lines.push(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`used unshared by [${g.usedUnsharedBy.join(', ')}]`);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
lines.push(` ✗ ${g.package} — shared only by ${g.sharedBy}, unused by all other MFs`);
|
|
64
|
-
}
|
|
76
|
+
for (const g of ghostShares) {
|
|
77
|
+
lines.push(`✗ Ghost Share — ${g.package}`);
|
|
78
|
+
lines.push(` shared only by: ${g.sharedBy}`);
|
|
79
|
+
if (g.usedUnsharedBy.length > 0) {
|
|
80
|
+
lines.push(` used unshared by: [${g.usedUnsharedBy.join(', ')}]`);
|
|
65
81
|
}
|
|
82
|
+
else {
|
|
83
|
+
lines.push(` unused by all other MFs`);
|
|
84
|
+
}
|
|
85
|
+
lines.push(` → One-sided coupling with no federation benefit`);
|
|
86
|
+
lines.push(` 💡 Fix: Remove "${g.package}" from ${g.sharedBy}'s shared config`);
|
|
66
87
|
lines.push('');
|
|
67
88
|
}
|
|
68
|
-
// ── 5. Summary
|
|
69
|
-
|
|
89
|
+
// ── 5. Score + Summary ───────────────────────────────────────────────────
|
|
90
|
+
const score = scoreFederationReport(report);
|
|
91
|
+
lines.push(HR);
|
|
92
|
+
lines.push(formatScoreBlock(score, 'version conflicts', 'singleton mismatches, host gaps', 'ghost shares'));
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(`Total: ${summary.totalManifests} MFs, ` +
|
|
70
95
|
`${summary.versionConflictsCount} version conflicts, ` +
|
|
71
96
|
`${summary.singletonMismatchesCount} singleton mismatches, ` +
|
|
72
97
|
`${summary.hostGapsCount} host gaps, ` +
|
|
@@ -74,4 +99,20 @@ export function formatFederationReport(report) {
|
|
|
74
99
|
lines.push('');
|
|
75
100
|
return lines.join('\n');
|
|
76
101
|
}
|
|
102
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
103
|
+
/** Pick the version string with the highest semver major (and minor) as the suggested fix. */
|
|
104
|
+
function pickHighestRequiredVersion(c) {
|
|
105
|
+
const versions = Object.values(c.versions);
|
|
106
|
+
const sorted = versions.slice().sort((a, b) => {
|
|
107
|
+
const clean = (v) => v.replace(/^[\^~>=<\s]+/, '').split('.').map(Number);
|
|
108
|
+
const [aMaj = 0, aMin = 0, aPatch = 0] = clean(a);
|
|
109
|
+
const [bMaj = 0, bMin = 0, bPatch = 0] = clean(b);
|
|
110
|
+
if (bMaj !== aMaj)
|
|
111
|
+
return bMaj - aMaj;
|
|
112
|
+
if (bMin !== aMin)
|
|
113
|
+
return bMin - aMin;
|
|
114
|
+
return bPatch - aPatch;
|
|
115
|
+
});
|
|
116
|
+
return sorted[0];
|
|
117
|
+
}
|
|
77
118
|
//# sourceMappingURL=format-federation-report.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-federation-report.js","sourceRoot":"","sources":["../../src/reporter/format-federation-report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format-federation-report.js","sourceRoot":"","sources":["../../src/reporter/format-federation-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,14 +5,21 @@ export interface FormatReportContext {
|
|
|
5
5
|
filesScanned?: number;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Formats a ProjectReport as
|
|
8
|
+
* Formats a ProjectReport as actionable diagnostic cards.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* Each finding is rendered as a card with:
|
|
11
|
+
* - Issue title + package name
|
|
12
|
+
* - Concrete detail (version numbers, import count, etc.)
|
|
13
|
+
* - Risk description (what breaks at runtime)
|
|
14
|
+
* - Ready-to-paste fix snippet
|
|
15
|
+
*
|
|
16
|
+
* Section order (by severity):
|
|
17
|
+
* 1. Version mismatch — most dangerous, sharing silently broken
|
|
18
|
+
* 2. Unused shared — easy wins, safe to remove
|
|
19
|
+
* 3. Candidates — not shared but should be
|
|
20
|
+
* 4. Singleton risks — missing singleton: true
|
|
21
|
+
* 5. Eager risks — eager without singleton
|
|
22
|
+
* 6. Summary line
|
|
16
23
|
*/
|
|
17
24
|
export declare function formatReport(report: ProjectReport, ctx?: FormatReportContext): string;
|
|
18
25
|
//# sourceMappingURL=format-report.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-report.d.ts","sourceRoot":"","sources":["../../src/reporter/format-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"format-report.d.ts","sourceRoot":"","sources":["../../src/reporter/format-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAqGrF"}
|
|
@@ -1,73 +1,102 @@
|
|
|
1
|
+
import { getDiagnostic, buildFixSnippet } from './diagnostics.js';
|
|
2
|
+
import { scoreProjectReport, formatScoreBlock } from './scoring.js';
|
|
3
|
+
const HR = '─'.repeat(60);
|
|
1
4
|
/**
|
|
2
|
-
* Formats a ProjectReport as
|
|
5
|
+
* Formats a ProjectReport as actionable diagnostic cards.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Each finding is rendered as a card with:
|
|
8
|
+
* - Issue title + package name
|
|
9
|
+
* - Concrete detail (version numbers, import count, etc.)
|
|
10
|
+
* - Risk description (what breaks at runtime)
|
|
11
|
+
* - Ready-to-paste fix snippet
|
|
12
|
+
*
|
|
13
|
+
* Section order (by severity):
|
|
14
|
+
* 1. Version mismatch — most dangerous, sharing silently broken
|
|
15
|
+
* 2. Unused shared — easy wins, safe to remove
|
|
16
|
+
* 3. Candidates — not shared but should be
|
|
17
|
+
* 4. Singleton risks — missing singleton: true
|
|
18
|
+
* 5. Eager risks — eager without singleton
|
|
19
|
+
* 6. Summary line
|
|
10
20
|
*/
|
|
11
21
|
export function formatReport(report, ctx) {
|
|
12
22
|
const lines = [];
|
|
13
23
|
const { unused, candidates, mismatched, singletonRisks, eagerRisks, summary } = report;
|
|
14
|
-
|
|
15
|
-
const header = buildHeader(ctx);
|
|
16
|
-
lines.push('', header, '');
|
|
24
|
+
lines.push('', buildHeader(ctx), HR, '');
|
|
17
25
|
const hasFindings = mismatched.length > 0 ||
|
|
18
26
|
unused.length > 0 ||
|
|
19
27
|
candidates.length > 0 ||
|
|
20
28
|
singletonRisks.length > 0 ||
|
|
21
29
|
eagerRisks.length > 0;
|
|
22
30
|
if (!hasFindings) {
|
|
23
|
-
lines.push(' No issues found. Shared config looks good.');
|
|
31
|
+
lines.push(' ✓ No issues found. Shared config looks good.');
|
|
32
|
+
lines.push('');
|
|
24
33
|
}
|
|
25
34
|
// ── 1. Version mismatch ────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
for (const m of mismatched) {
|
|
36
|
+
const { risk } = getDiagnostic(m.package, 'mismatch');
|
|
37
|
+
lines.push(`⚠ Version Mismatch — ${m.package}`);
|
|
38
|
+
lines.push(` configured: ${m.configured} | installed: ${m.installed}`);
|
|
39
|
+
lines.push(` → Risk: ${risk}`);
|
|
40
|
+
lines.push(` 💡 Fix:`);
|
|
41
|
+
for (const l of buildFixSnippet(m.package, { singleton: true, requiredVersion: m.configured }).split('\n')) {
|
|
42
|
+
lines.push(` ${l}`);
|
|
30
43
|
}
|
|
31
44
|
lines.push('');
|
|
32
45
|
}
|
|
33
46
|
// ── 2. Unused shared ──────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
47
|
+
for (const u of unused) {
|
|
48
|
+
const { risk } = getDiagnostic(u.package, 'unused');
|
|
49
|
+
const note = u.singleton ? 'shared as singleton' : 'shared without singleton';
|
|
50
|
+
lines.push(`✗ Unused Shared — ${u.package}`);
|
|
51
|
+
lines.push(` 0 imports, ${note}`);
|
|
52
|
+
lines.push(` → ${risk}`);
|
|
53
|
+
lines.push(` 💡 Fix: Remove "${u.package}" from shared config`);
|
|
40
54
|
lines.push('');
|
|
41
55
|
}
|
|
42
56
|
// ── 3. Candidates ─────────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
for (const c of candidates) {
|
|
58
|
+
const { risk } = getDiagnostic(c.package, 'candidate');
|
|
59
|
+
const fileCount = c.files.length;
|
|
60
|
+
const filesLabel = fileCount === 1 ? '1 file' : `${fileCount} files`;
|
|
61
|
+
const viaLabel = c.via === 'reexport' ? ` via re-export in ${c.files[0]}` : '';
|
|
62
|
+
lines.push(`→ Not Shared — ${c.package} (${c.importCount} imports in ${filesLabel}${viaLabel})`);
|
|
63
|
+
lines.push(` → Risk: ${risk}`);
|
|
64
|
+
lines.push(` 💡 Fix:`);
|
|
65
|
+
for (const l of buildFixSnippet(c.package, { singleton: true }).split('\n')) {
|
|
66
|
+
lines.push(` ${l}`);
|
|
50
67
|
}
|
|
51
68
|
lines.push('');
|
|
52
69
|
}
|
|
53
70
|
// ── 4. Singleton risks ────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
for (const r of singletonRisks) {
|
|
72
|
+
const { risk } = getDiagnostic(r.package, 'singleton-risk');
|
|
73
|
+
lines.push(`⚠ Singleton Risk — ${r.package}`);
|
|
74
|
+
lines.push(` singleton: true is missing`);
|
|
75
|
+
lines.push(` → Risk: ${risk}`);
|
|
76
|
+
lines.push(` 💡 Fix:`);
|
|
77
|
+
for (const l of buildFixSnippet(r.package, { singleton: true }).split('\n')) {
|
|
78
|
+
lines.push(` ${l}`);
|
|
58
79
|
}
|
|
59
80
|
lines.push('');
|
|
60
81
|
}
|
|
61
82
|
// ── 5. Eager risks ────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
for (const r of eagerRisks) {
|
|
84
|
+
const { risk } = getDiagnostic(r.package, 'eager-risk');
|
|
85
|
+
lines.push(`⚠ Eager Risk — ${r.package}`);
|
|
86
|
+
lines.push(` eager: true without singleton: true`);
|
|
87
|
+
lines.push(` → Risk: ${risk}`);
|
|
88
|
+
lines.push(` 💡 Fix:`);
|
|
89
|
+
for (const l of buildFixSnippet(r.package, { singleton: true, eager: true }).split('\n')) {
|
|
90
|
+
lines.push(` ${l}`);
|
|
66
91
|
}
|
|
67
92
|
lines.push('');
|
|
68
93
|
}
|
|
69
|
-
// ── 6. Summary
|
|
70
|
-
|
|
94
|
+
// ── 6. Score + Summary ───────────────────────────────────────────────────
|
|
95
|
+
const score = scoreProjectReport(report);
|
|
96
|
+
lines.push(HR);
|
|
97
|
+
lines.push(formatScoreBlock(score, 'version mismatch', 'singleton gaps, duplicate libs', 'over-sharing'));
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push(`Total: ${summary.totalShared} shared, ${summary.usedShared} used, ` +
|
|
71
100
|
`${summary.unusedCount} unused, ${summary.candidatesCount} candidates, ` +
|
|
72
101
|
`${summary.mismatchedCount} mismatch, ${summary.eagerRisksCount} eager risks`);
|
|
73
102
|
lines.push('');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-report.js","sourceRoot":"","sources":["../../src/reporter/format-report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format-report.js","sourceRoot":"","sources":["../../src/reporter/format-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ProjectReport, FederationReport } from '../types.js';
|
|
2
|
+
export interface RiskScore {
|
|
3
|
+
/** 0–100. Higher is better. */
|
|
4
|
+
score: number;
|
|
5
|
+
/** Verbal label derived from score range. */
|
|
6
|
+
label: 'HEALTHY' | 'GOOD' | 'RISKY' | 'CRITICAL';
|
|
7
|
+
/** Count of high-severity findings (version mismatches). */
|
|
8
|
+
high: number;
|
|
9
|
+
/** Count of medium-severity findings (singleton gaps, duplicate libs). */
|
|
10
|
+
medium: number;
|
|
11
|
+
/** Count of low-severity findings (over-sharing). */
|
|
12
|
+
low: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function scoreProjectReport(report: ProjectReport): RiskScore;
|
|
15
|
+
export declare function scoreFederationReport(report: FederationReport): RiskScore;
|
|
16
|
+
/**
|
|
17
|
+
* Renders the score block appended to reports.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* Score: 62/100 🟠 RISKY
|
|
21
|
+
*
|
|
22
|
+
* Issues:
|
|
23
|
+
* 🔴 1 high — version mismatch
|
|
24
|
+
* 🟠 2 medium — singleton gaps, duplicate libs
|
|
25
|
+
* 🟡 0 low — over-sharing
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatScoreBlock(score: RiskScore, highLabel: string, mediumLabel: string, lowLabel: string): string;
|
|
28
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/reporter/scoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,WAAW,SAAS;IACxB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IACjD,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;CACb;AAsBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAKnE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAKzE;AA0BD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,MAAM,CAYR"}
|