@tonoizer/mfdoctor finds config, sharing, runtime, manifest, and output
problems in Module Federation projects. Supported bundlers (first-class
adapter + real build+MFDoctor CI gate): Vite, Rspack, Rsbuild, and Webpack.
Partial (adapter present, limited rules/fixtures, not a full CI gate):
Modern.js, Nuxt 3/4, and Rolldown-integrated Vite / Vite Plus. Machine-readable
status:
fixtures/compatibility-matrix.json;
human matrix:
compatibility.
Install as a devDependency. MFDoctor is build/CI-only: adapters run after emit in Node and are not part of the browser bundle. They add CI time, not runtime size or performance cost. The build plugin is the primary integration; the CLI complements it for config, workspace, runtime, and deployed checks.
Agents: follow the two-tier loop.
mfdoctor check is config/static only — do not claim green from check alone.
After fixes, require plugin emit (build with a MFDoctor adapter) and, in
monorepos, mfdoctor workspace. Treat exit 2 and
doctor/partial-analysis
as incomplete, not a pass. Read findings (or JSON/diagnostics), open the linked
rule docs, apply the fix (or an intentional
governance mute when asked), and
rebuild until policy exits 0. Quiet success prints nothing.
Register MFDoctor next to your Module Federation plugin. It runs after emit, prints all findings once at the end of the build (severity, rule, message, fix, docs links), then fails when policy says so — only after every finding is collected. Clean builds stay quiet by default.
| Bundler | Status | Notes |
|---|---|---|
| Vite / Vite 5 CommonJS | supported | Primary CI cells in fixtures/compatibility-matrix.json |
| Rspack / Rsbuild / Webpack | supported | First-class adapters; production build+MFDoctor gates |
| Rolldown / Vite Plus | partial | Same @tonoizer/mfdoctor/vite entry; no dedicated Rolldown CI smoke |
| Modern.js | partial | Rspack-under-the-hood smoke; App Tools blocked by lockfile trustPolicy |
| Nuxt 3/4 | partial | Adapter + unit contract; upstream app build baseline-blocked |
Partial means an adapter exists and some coverage is present, but rule depth,
fixtures, and CI evidence are not on par with the supported cells — a green
mfdoctor check / plugin emit on a partial stack is not as trustworthy as on
Vite / Rspack / Rsbuild / Webpack. See the matrix fixture and
compatibility for the live rows.
Vite (supported; Rolldown-integrated Vite and Vite Plus are partial — same entry)
import { federation } from "@module-federation/vite";
import { federationDoctor } from "@tonoizer/mfdoctor/vite";
plugins: [federation(mfOptions), federationDoctor({ moduleFederation: mfOptions })];Nuxt 3/4 (partial — public vite:extendConfig adapter; limited CI evidence)
import moduleFederationDoctor from "@tonoizer/mfdoctor/nuxt";
export default defineNuxtConfig({
modules: ["@module-federation/nuxt", [moduleFederationDoctor, { moduleFederation: mfOptions }]],
});The official Nuxt Module Federation module still owns federation. The MFDoctor module observes both client and SSR Vite builds without owning the federation plugin or duplicating its configuration.
Multiple Module Federation instances
When one compiler/config intentionally contains more than one independently configured federation graph, pass the instances explicitly when the bundler does not expose their public options:
federationDoctor({
moduleFederationInstances: [
{ pluginName: "ModuleFederationPlugin", config: checkoutMfOptions },
{ pluginName: "ModuleFederationPlugin", config: catalogMfOptions },
],
});Webpack, Rspack, and Vite-family adapters also read public plugin configs when available. MFDoctor derives stable per-instance IDs, keeps manifests/stats/build outputs and shared-version evidence scoped, and reports identical duplicate registrations separately. Workspace and UI federation graphs include the instance scope in every affected edge and node; Nuxt client/SSR outputs are aggregated deterministically.
Rspack (supported — direct @rspack/core; not replaced by Modern.js)
import { ModuleFederationPlugin } from "@module-federation/enhanced/rspack";
import { moduleFederationDoctorPlugin } from "@tonoizer/mfdoctor/rspack";
plugins: [
new ModuleFederationPlugin(mfOptions),
moduleFederationDoctorPlugin({ moduleFederation: mfOptions }),
];Rsbuild (supported)
import { pluginModuleFederation } from "@module-federation/rsbuild-plugin";
import { pluginModuleFederationDoctor } from "@tonoizer/mfdoctor/rsbuild";
plugins: [
pluginModuleFederation(mfOptions),
pluginModuleFederationDoctor({ moduleFederation: mfOptions }),
];Webpack (supported)
import { ModuleFederationPlugin } from "@module-federation/enhanced/webpack";
import { ModuleFederationDoctorPlugin } from "@tonoizer/mfdoctor/webpack";
plugins: [
new ModuleFederationPlugin(mfOptions),
ModuleFederationDoctorPlugin({ moduleFederation: mfOptions }),
];Modern.js (partial — adapter + Rspack-under-the-hood smoke; does not hide /rspack)
import { appTools } from "@modern-js/app-tools";
import { moduleFederationPlugin } from "@module-federation/modern-js-v3";
import { moduleFederationDoctorPlugin } from "@tonoizer/mfdoctor/modern";
plugins: [
appTools(),
moduleFederationPlugin({ config: mfOptions, ssr: false }),
moduleFederationDoctorPlugin({ moduleFederation: mfOptions }),
];A real @modern-js/app-tools CI cell is blocked by this repo's
trustPolicy: no-downgrade (current App Tools dropped npm provenance after
2.63.3). The compatibility smoke stays Rspack-under-the-hood.
CI is auto-detected from the environment (CI, GITHUB_ACTIONS, and other
common provider signals). In CI, MFDoctor fails on error findings and includes
SARIF by default — you do not need mode: "ci" in plugin config. Local
development defaults to failOn: "never" so findings print without breaking
the build. Override with --ci, mode: "ci", mode: "development", or
failOn.
Quiet success is the default: zero findings print nothing. Use --verbose,
printLog: { success: true }, quiet: false, or MFDOCTOR_QUIET=0 for the
legacy "no findings" line. MFDOCTOR_QUIET=1 forces quiet.
When a rule is known and accepted (for example a host that keeps direct
remoteEntry URLs), turn that rule off — do not disable MFDoctor:
federationDoctor({
moduleFederation: mfOptions,
rules: {
// Intentional: no manifest server in this app yet.
"config/remote-manifest-recommended": "off",
},
});See Governance: suppressions and allowlists
for severity overrides, policy packs, fingerprint baselines, failOn, and the
canonical examples/mixed-federation host pattern. Full rule catalog:
Rule reference.
Use the CLI when you are not running a bundler build, or for cross-project and deployed checks.
Name clash: npx mf-doctor is a different package (tiagocastro070), not this
project. This package is @tonoizer/mfdoctor (CLI binary mfdoctor). Install as
a dependency and run via package-manager exec (pnpm exec mfdoctor,
npx mfdoctor, etc.) — not npx mf-doctor.
pnpm add -D @tonoizer/mfdoctor
pnpm exec mfdoctor check --ci
pnpm exec mfdoctor check --format terminal,json,sarif
pnpm exec mfdoctor check --baseline ./mfdoctor.baseline.json
pnpm exec mfdoctor check --verbose
pnpm exec mfdoctor workspace
pnpm exec mfdoctor federation --workspace
pnpm exec mfdoctor federation ".mf/doctor/**/project.json"
pnpm exec mfdoctor baseline generate .mf/doctor/report.json
pnpm exec mfdoctor runtime ./.mf/observability/latest.json
pnpm exec mfdoctor probe https://cdn.example.com/mf-manifest.json --remote-entry
pnpm exec mfdoctor compare https://cdn.example.com/mf-manifest.json https://canary.example.com/mf-manifest.json
pnpm exec mfdoctor rules config/name-requiredFor a coding-agent or other non-interactive handoff, discover the supported contract first, then keep machine-readable artifacts and prompts on disk:
pnpm exec mfdoctor capabilities
pnpm exec mfdoctor check --ci --format terminal,json,sarif \
--diagnostics-dir .mf/doctor/diagnostics
pnpm exec mfdoctor prompt --finding config/name-required .mf/doctor/report.jsonThe published package ships the same playbook as AGENTS.md and
the Cursor/agent skill at skills/mfdoctor/SKILL.md
(capabilities → check JSON → prompt → rebuild). Hard rules: no suppressions and
no probe unless the user asked; do not claim green from check alone.
capabilities is versioned JSON and does not load project configuration or use
the network. A check exits 0 when policy passes, 1 when policy fails, and
2 when analysis is incomplete. The diagnostics directory contains bounded
report.json, summary.md, and prompts/*.md (default top-3 prompts; pass
--diagnostics-prompts <n> up to 25, or set MFDOCTOR_DIAGNOSTICS_PROMPTS, to
dump more). JSON and SARIF remain stable machine-readable contracts, so agents
do not need to scrape terminal output.
Supported report formats are terminal, JSON, and SARIF only — there
is no HTML report or --ui dashboard. For a programmatic remotes/shared graph,
use buildUiPayload and schemas/ui.schema.json (see report schemas in the
docs).
| Command | When to use it |
|---|---|
| Plugin on build | Gate the real emit; strongest artifact evidence |
check |
Offline config analysis without a full bundler run |
workspace |
One-shot host↔remote gate; auto-discovers project.json |
federation |
Same gate with --workspace or manual globs (escape hatch) |
baseline |
Generate/update fingerprint baselines for incremental CI |
runtime |
Correlate an Observability Plugin export with project facts |
probe |
Inspect a deployed manifest / remoteEntry HEAD (network) |
compare |
Diff deployed manifests (network; same safety as probe) |
check, workspace, federation, and runtime stay offline. probe and
compare are the only commands that fetch over the network, and they never
execute remote JavaScript. Exit codes: 0 pass, 1 policy fail (or compare
drift), 2 analysis incomplete.
Fingerprint baselines keep known debt visible in reports without failing policy
by default — see baselines and
governance.
Host teams do not need this repository's Vite Plus / setup-vp toolchain.
Install @tonoizer/mfdoctor, register a bundler adapter, build, then gate:
pnpm add -D @tonoizer/mfdoctor
# register @tonoizer/mfdoctor/{vite,webpack,rspack,rsbuild} next to your MF plugin
pnpm run build
pnpm exec mfdoctor workspace --format terminal,json,sarifCopy-paste GitHub Actions (Node + pnpm/npm/yarn + the workspace-federation-gate
Action): CLI / GitHub Actions
and examples/ci/github-actions-mfdoctor.yml.
runtime accepts one JSON Observability report, an array of reports, or a
{"report": ...} / {"reports": [...]} envelope. Current upstream
Observability 2.5.3 reports are supported, along with the legacy MFDoctor v1
shape (success, init, factory, and old diagnosis/module fields). Partial
reports are imported as partial evidence; missing fields never count as a
pass. Missing shared lifecycle data on partial/old/preview runtimes is marked
sharedCompleteness: unknown, not healthy. Unknown future shapes and build
reports fail with a typed error. The general evidence reader
(readEvidenceDocument) rejects Observability reports and points callers at
parseRuntimeTraces / loadRuntimeTraceFile. Docs:
Observability latest.json → mfdoctor runtime.
Runtime imports are opt-in and local only. MFDoctor does not fetch, upload, open
a browser, or execute report contents. Stored/output evidence is bounded and
redacts credentials, secret query values, private paths, and stack traces.
Invalid opt-in runtimeTrace paths do not break offline check; they simply
omit runtime import hints.
Share org governance with built-in profiles (recommended, strict, demo,
production) and package-level policy packs via extends. recommended
matches the catalog defaults, strict raises most advisory severities for CI,
demo quiets selected local-demo nudges, and production raises selected
enable-this recommendations. Use profile: "demo" or profile: "production"
as a top-level shortcut when the overlay should follow extends; local
rules still win, and a demo profile resolves to the production overlay in CI.
Profiles only adjust recommendation severities and bounded rule options;
correctness findings stay on. Packs can ship severity maps plus custom
defineRule plugins. See policy packs.
- Core config: names, exposes, remotes, scopes, runtime plugins, public paths.
- Shared modules: versions, singleton/eager use, providers, and tree shaking.
- Runtime modes: startup strategy, snapshots, external runtime, and recovery.
- Vite details: CSS bundling, parser timeouts, and Vite-only switches.
- Build output: manifests, remote entries, type archives, assets, and metadata.
- Whole federation: cross-project name, version, scope, and provider conflicts.
MF runtimePlugins in bundler config are checked at build time. Runtime-only
apps (createInstance / runtime plugins without a Vite/Rspack/Rsbuild/Webpack MF
build plugin) are out of scope for first-class support — use Observability +
mfdoctor runtime instead of shipping MFDoctor into the browser. See
Observability → runtime,
limitations, and
#34.
Every built-in rule has an issue, impact, fix, category, and source link. See the rule reference and Get started for setup, CI, and the fix-until-exit-0 loop.
Requires Node >=22.12.0. Vite+ manages the
repository's Node.js, package manager, build, test, lint, and format toolchain. The
workspace policy pins pnpm to 11.17.0, delays new dependency releases by ten days,
and requires explicit approval for dependency build scripts. See the
compatibility matrix and
fixtures/compatibility-matrix.json
for supported / partial / unsupported cells (Vite, Rspack, Rsbuild, Webpack
supported; Modern.js, Nuxt, Rolldown / Vite Plus partial; npm / yarn
consumer notes; terminal / JSON / SARIF on CI).
vp install
vp pack
vp exec playwright install chromium
vp run check
vp run release:dry-runExamples:
examples/mixed-federation— healthy Vite + Rspack + Rsbuild e2e pathexamples/nested-federation— nested Vite host → Vite/Rsbuild → Rspack/Webpack; runvp run demo:nestedorvp run test:nestedexamples/compatibility/webpack— Webpack build+MFDoctor smoke for the matrixexamples/compatibility/nuxt— Nuxt module adapter smoke (partial; Vite-under-the-hood)examples/compatibility/rolldown— Vite Plus / Rolldown smoke (partial; same Vite entry)examples/mixed-federation-issues— same flat topology with intentional MFDoctor findings; runvp run demo:mixed-issuesexamples/standalone-findings— per-bundler Vite/Webpack/Rspack/Rsbuild cells plus a partial Modern.js afterEmit stub that emit visible MFDoctor findings; runvp run demo:standaloneexamples/showcase— one-rule CLI fixtures + runtime green/fail demos; runvp run demo:showcaseexamples/ci/github-actions-mfdoctor.yml— copy-paste consumer CI (no Vite Plus /setup-vp); see CLI / GitHub Actions- From
examples/:vp run demoruns showcase + standalone + mixed-issues + nested (orvp run demo:examplesfrom the repo root) - See Examples for the full catalog. The
one-command full E2E gate is
vp run test:e2e; it builds the green, intentional-finding, nested, and compatibility cells, runs cross-app gates, and executes the green and negative Playwright runtime paths.vp run test:gigaremains as a compatibility alias for existing automation.
MFDoctor-specific agent UX prefers CLI/plugin finding output (rule id, fix,
MFDoctor docs URL, official MF sources, exit codes) plus an offline health score
footer (Score: N/100) and top-3 copy-paste agent prompts on local runs. CI
hides prompts by default (opt in with --prompt, or dump via
--diagnostics-dir). Use --no-score / --no-prompt to hide terminal footers;
JSON reports still include summary.score. Offline: mfdoctor prompt --finding <id> and --diagnostics-dir for handoff dumps. After install, agents should read
AGENTS.md or skills/mfdoctor/SKILL.md from the package. For
Module Federation concepts, use the upstream mf skill (this repository vendors
it under .agents/skills/mf for maintainers). Upstream evidence for rule work
lives in the
contribution guide.
New contributors are welcome. Please read the Contributing Guide.
The initial idea was inspired by Rsdoctor:
“Something like RS Doctor, but just for Module Federation.”
Getting something useful out of Module Federation can be tricky during initial setup: the important details are spread across configuration, shared dependencies, runtime behavior, manifests, and build output. The goal here was to bring that kind of focused diagnostic experience to Module Federation. Presets and targeted scans make the nitty-gritty visible early, without requiring users to dive deeply into federation or bundler internals first.
That idea shaped MFDoctor into a diagnostics tool focused on the configuration, sharing, runtime, manifest, and output problems unique to Module Federation projects. Thanks to the Rsdoctor team for the inspiration.
Please follow the Code of Conduct.
MIT © 2026 Kevin Beier and contributors.