Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8686d8e
feat(repack): add federation manifest schema, builder and emitter
gedu Sep 21, 2026
a5b01b6
feat(repack): wire opt-in manifest option into module federation plugins
gedu Sep 21, 2026
75c9a1a
docs(repack): document the opt-in federation manifest
gedu Sep 21, 2026
6205e34
feat(repack): add federation manifest inspection and doctor commands
gedu Sep 21, 2026
544fc8e
fix(repack): exclude synthetic deep-import keys from the federation m…
gedu Sep 21, 2026
12ddc48
style(repack): format federation manifest shared assertion
gedu Sep 21, 2026
c5df67f
fix(repack): report conventional host-eager/remote-lazy as EAGER_ADVI…
gedu Sep 21, 2026
6c06f0b
refactor(repack): share installed-version resolver via lazy builtin a…
gedu Sep 21, 2026
3e01004
feat(repack): add defineShared exact-pin shared-config helper
gedu Sep 21, 2026
7d06ba8
feat(repack): add repack-federation.json loader with zero-flag doctor…
gedu Sep 21, 2026
caefb4f
feat(repack): doctor pairwise opt-in, role-aware eager advisory, host…
gedu Sep 21, 2026
bf78dee
feat(repack): add plugin shared extraction and doctor --dry-run pre-b…
gedu Sep 21, 2026
eaf49ea
feat(repack): add runtime-only --standalone mode via env.argv with to…
gedu Sep 21, 2026
622501d
feat(repack): add feature-folder static scanner with dynamic-import a…
gedu Sep 21, 2026
c664531
feat(repack): add federation-init plan engine (idempotent, diff-befor…
gedu Sep 22, 2026
04acc00
feat(repack): register flat federation-init command and docs
gedu Sep 22, 2026
b9a2b08
refactor(testers): migrate federation tester configs to defineShared
gedu Sep 22, 2026
49716da
fix(repack): pass CLI positional args and discover init-generated con…
gedu Sep 22, 2026
6b6758f
docs(repack): federation workspace workflow guide and design-doc corr…
gedu Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/federation-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@callstack/repack': minor
---

Add an opt-in `manifest` option to both module federation plugins. When set, the build emits `repack-federation-manifest.json` next to the bundle: shared dependencies report the versions actually installed in `node_modules` instead of the `*` range the plugins configure by default, and an additive `reactNative` block lists the native modules found in the module graph. Field shapes follow the upstream `mf-manifest.json` spec, so existing tooling can parse the file as-is. With the option absent, builds are byte-identical to before.

Two commands consume the manifest. `npx react-native federation-manifest <file|dir|url>` prints a human-readable summary of what a host or remote shipped, or the raw document with `--json`. `npx react-native federation-doctor --host <source> --remotes <list>` compares a host manifest against its remotes and reports singleton version drift, `singleton`/`eager` mismatches, unresolvable `requiredVersion` ranges, native modules the host does not declare, and remotes that ship no manifest. It exits 1 on drift and 2 when a check could not run, so it works as a CI gate; `--format json` and `--allow-missing-manifests` cover scripts and gradual rollout.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,13 @@ packages/**/docs

# watchman
.watchman-cookie*

# Fixture node_modules for federation manifest / defineShared version tests
!packages/repack/src/plugins/__tests__/__fixtures__/manifest-context/node_modules/
!packages/repack/src/utils/__tests__/__fixtures__/define-shared-app/node_modules/
!packages/repack/src/utils/__tests__/__fixtures__/define-shared-split-app/node_modules/
!packages/repack/src/commands/federation/__fixtures__/dry-run-workspace/apps/host/node_modules/
!packages/repack/src/commands/federation/__fixtures__/dry-run-workspace/apps/remote-clean/node_modules/
!packages/repack/src/commands/federation/__fixtures__/dry-run-workspace/apps/remote-drift/node_modules/
!packages/repack/src/commands/federation/__fixtures__/init-workspace/apps/host/node_modules/
!packages/repack/src/commands/federation/__fixtures__/init-workspace/apps/remote-drift/node_modules/
1 change: 1 addition & 0 deletions agent_context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ works, and is kept in sync with the implementation as it evolves.
| Folder | Topic |
| --- | --- |
| [rspackv2-jul2026](./rspackv2-jul2026/design.md) | Dual Rspack 1.x/2.x support |
| [federation-tools](./federation-tools/design.md) | Federation manifest, doctor, shared-config retrofit, dev runner |
318 changes: 318 additions & 0 deletions agent_context/federation-tools/design.md

Large diffs are not rendered by default.

57 changes: 16 additions & 41 deletions apps/tester-federation-v2/configs/rspack.host-app.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import * as Repack from '@callstack/repack';
import rspack from '@rspack/core';
import reactPkg from 'react/package.json' with { type: 'json' };
import reactNativePkg from 'react-native/package.json' with { type: 'json' };
import pkg from '../package.json' with { type: 'json' };

// Shared dependencies are versionless here: defineShared pins every entry to
// the exact version installed in this workspace, so host and remotes always
// agree without hand-maintained literals.
const SHARED_DEPS = [
'react',
'react-native',
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-safe-area-context',
'react-native-screens',
];

export default Repack.defineRspackConfig((env) => {
const { mode, context, platform } = env;
Expand Down Expand Up @@ -52,44 +61,10 @@ export default Repack.defineRspackConfig((env) => {
MiniApp: `MiniApp@http://localhost:8082/${platform}/mf-manifest.json`,
},
dts: false,
shared: {
react: {
singleton: true,
eager: true,
version: reactPkg.version,
requiredVersion: reactPkg.version,
},
'react-native': {
singleton: true,
eager: true,
version: reactNativePkg.version,
requiredVersion: reactNativePkg.version,
},
'@react-navigation/native': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native'],
requiredVersion: pkg.dependencies['@react-navigation/native'],
},
'@react-navigation/native-stack': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native-stack'],
requiredVersion: pkg.dependencies['@react-navigation/native-stack'],
},
'react-native-safe-area-context': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-safe-area-context'],
requiredVersion: pkg.dependencies['react-native-safe-area-context'],
},
'react-native-screens': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-screens'],
requiredVersion: pkg.dependencies['react-native-screens'],
},
},
shared: Repack.defineShared(SHARED_DEPS, {
context,
role: 'host',
}),
}),
// silence missing @react-native-masked-view optionally required by @react-navigation/elements
new rspack.IgnorePlugin({
Expand Down
59 changes: 18 additions & 41 deletions apps/tester-federation-v2/configs/rspack.mini-app.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import * as Repack from '@callstack/repack';
import rspack from '@rspack/core';
import reactPkg from 'react/package.json' with { type: 'json' };
import reactNativePkg from 'react-native/package.json' with { type: 'json' };
import pkg from '../package.json' with { type: 'json' };

// Shared dependencies are versionless here: defineShared pins every entry to
// the exact version installed in this workspace, so host and remotes always
// agree without hand-maintained literals.
const SHARED_DEPS = [
'react',
'react-native',
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-safe-area-context',
'react-native-screens',
];

export default Repack.defineRspackConfig((env) => {
const { mode, context, platform } = env;
Expand Down Expand Up @@ -49,44 +58,12 @@ export default Repack.defineRspackConfig((env) => {
'./MiniAppNavigator': './src/mini/navigation/MainNavigator',
},
dts: false,
shared: {
react: {
singleton: true,
eager: false,
version: reactPkg.version,
requiredVersion: reactPkg.version,
},
'react-native': {
singleton: true,
eager: false,
version: reactNativePkg.version,
requiredVersion: reactNativePkg.version,
},
'@react-navigation/native': {
singleton: true,
eager: false,
version: pkg.dependencies['@react-navigation/native'],
requiredVersion: pkg.dependencies['@react-navigation/native'],
},
'@react-navigation/native-stack': {
singleton: true,
eager: false,
version: pkg.dependencies['@react-navigation/native-stack'],
requiredVersion: pkg.dependencies['@react-navigation/native-stack'],
},
'react-native-safe-area-context': {
singleton: true,
eager: false,
version: pkg.dependencies['react-native-safe-area-context'],
requiredVersion: pkg.dependencies['react-native-safe-area-context'],
},
'react-native-screens': {
singleton: true,
eager: false,
version: pkg.dependencies['react-native-screens'],
requiredVersion: pkg.dependencies['react-native-screens'],
},
},
shared: Repack.defineShared(SHARED_DEPS, {
context,
role: 'remote',
// `--standalone` is runtime-only (env.argv): never committed.
mode: env.argv?.standalone ? 'standalone' : 'federated',
}),
}),
// silence missing @react-native-masked-view optionally required by @react-navigation/elements
new rspack.IgnorePlugin({
Expand Down
57 changes: 16 additions & 41 deletions apps/tester-federation-v2/configs/webpack.host-app.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// @ts-check
import * as Repack from '@callstack/repack';
import reactPkg from 'react/package.json' with { type: 'json' };
import reactNativePkg from 'react-native/package.json' with { type: 'json' };
import webpack from 'webpack';
import pkg from '../package.json' with { type: 'json' };

// Shared dependencies are versionless here: defineShared pins every entry to
// the exact version installed in this workspace, so host and remotes always
// agree without hand-maintained literals.
const SHARED_DEPS = [
'react',
'react-native',
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-safe-area-context',
'react-native-screens',
];

export default Repack.defineWebpackConfig((env) => {
const { mode, context, platform } = env;
Expand Down Expand Up @@ -48,44 +57,10 @@ export default Repack.defineWebpackConfig((env) => {
MiniApp: `MiniApp@http://localhost:8082/${platform}/mf-manifest.json`,
},
dts: false,
shared: {
react: {
singleton: true,
eager: true,
version: reactPkg.version,
requiredVersion: reactPkg.version,
},
'react-native': {
singleton: true,
eager: true,
version: reactNativePkg.version,
requiredVersion: reactNativePkg.version,
},
'@react-navigation/native': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native'],
requiredVersion: pkg.dependencies['@react-navigation/native'],
},
'@react-navigation/native-stack': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native-stack'],
requiredVersion: pkg.dependencies['@react-navigation/native-stack'],
},
'react-native-safe-area-context': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-safe-area-context'],
requiredVersion: pkg.dependencies['react-native-safe-area-context'],
},
'react-native-screens': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-screens'],
requiredVersion: pkg.dependencies['react-native-screens'],
},
},
shared: Repack.defineShared(SHARED_DEPS, {
context,
role: 'host',
}),
}),
// silence missing @react-native-masked-view optionally required by @react-navigation/elements
new webpack.IgnorePlugin({
Expand Down
59 changes: 18 additions & 41 deletions apps/tester-federation-v2/configs/webpack.mini-app.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import * as Repack from '@callstack/repack';
import reactPkg from 'react/package.json' with { type: 'json' };
import reactNativePkg from 'react-native/package.json' with { type: 'json' };
import webpack from 'webpack';
import pkg from '../package.json' with { type: 'json' };

// Shared dependencies are versionless here: defineShared pins every entry to
// the exact version installed in this workspace, so host and remotes always
// agree without hand-maintained literals.
const SHARED_DEPS = [
'react',
'react-native',
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-safe-area-context',
'react-native-screens',
];

export default Repack.defineWebpackConfig((env) => {
const { mode, context, platform } = env;
Expand Down Expand Up @@ -47,44 +56,12 @@ export default Repack.defineWebpackConfig((env) => {
'./MiniAppNavigator': './src/mini/navigation/MainNavigator',
},
dts: false,
shared: {
react: {
singleton: true,
eager: false,
version: reactPkg.version,
requiredVersion: reactPkg.version,
},
'react-native': {
singleton: true,
eager: false,
version: reactNativePkg.version,
requiredVersion: reactNativePkg.version,
},
'@react-navigation/native': {
singleton: true,
eager: false,
version: pkg.dependencies['@react-navigation/native'],
requiredVersion: pkg.dependencies['@react-navigation/native'],
},
'@react-navigation/native-stack': {
singleton: true,
eager: false,
version: pkg.dependencies['@react-navigation/native-stack'],
requiredVersion: pkg.dependencies['@react-navigation/native-stack'],
},
'react-native-safe-area-context': {
singleton: true,
eager: false,
version: pkg.dependencies['react-native-safe-area-context'],
requiredVersion: pkg.dependencies['react-native-safe-area-context'],
},
'react-native-screens': {
singleton: true,
eager: false,
version: pkg.dependencies['react-native-screens'],
requiredVersion: pkg.dependencies['react-native-screens'],
},
},
shared: Repack.defineShared(SHARED_DEPS, {
context,
role: 'remote',
// `--standalone` is runtime-only (env.argv): never committed.
mode: env.argv?.standalone ? 'standalone' : 'federated',
}),
}),
// silence missing @react-native-masked-view optionally required by @react-navigation/elements
new webpack.IgnorePlugin({
Expand Down
62 changes: 17 additions & 45 deletions apps/tester-federation/configs/rspack.host-app.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import * as Repack from '@callstack/repack';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
import rspack from '@rspack/core';
import pkg from '../package.json' with { type: 'json' };

// Shared dependencies are versionless here: defineShared pins every entry to
// the exact version installed in this workspace, so host and remotes always
// agree without hand-maintained literals.
const SHARED_DEPS = [
'react',
'react-native',
'@react-navigation/native',
'@react-navigation/native-stack',
'react-native-safe-area-context',
'react-native-screens',
'@react-native-async-storage/async-storage',
];

export default Repack.defineRspackConfig((env) => {
const { mode, context, platform } = env;
Expand Down Expand Up @@ -43,50 +55,10 @@ export default Repack.defineRspackConfig((env) => {
}),
new Repack.plugins.ModuleFederationPluginV1({
name: 'HostApp',
shared: {
react: {
singleton: true,
eager: true,
requiredVersion: '19.2.3',
},
'react-native': {
singleton: true,
eager: true,
requiredVersion: '0.84.1',
},
'@react-navigation/native': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native'],
requiredVersion: pkg.dependencies['@react-navigation/native'],
},
'@react-navigation/native-stack': {
singleton: true,
eager: true,
version: pkg.dependencies['@react-navigation/native-stack'],
requiredVersion: pkg.dependencies['@react-navigation/native-stack'],
},
'react-native-safe-area-context': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-safe-area-context'],
requiredVersion: pkg.dependencies['react-native-safe-area-context'],
},
'react-native-screens': {
singleton: true,
eager: true,
version: pkg.dependencies['react-native-screens'],
requiredVersion: pkg.dependencies['react-native-screens'],
},
'@react-native-async-storage/async-storage': {
singleton: true,
eager: true,
version:
pkg.dependencies['@react-native-async-storage/async-storage'],
requiredVersion:
pkg.dependencies['@react-native-async-storage/async-storage'],
},
},
shared: Repack.defineShared(SHARED_DEPS, {
context,
role: 'host',
}),
}),
new rspack.IgnorePlugin({
resourceRegExp: /^@react-native-masked-view/,
Expand Down
Loading
Loading