Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
9c9eb54
feat(module-federation): add opencode config for alias resolution in …
ScriptedAlchemy Aug 28, 2025
6e829a7
feat(enhanced): improve module sharing with alias resolution
ScriptedAlchemy Sep 2, 2025
5d4cd6d
refactor(enhanced): simplify consume factorize; rename option; refine…
ScriptedAlchemy Sep 5, 2025
42a9b47
feat(enhanced): add alias-aware consume matching via resolverFactory;…
ScriptedAlchemy Sep 5, 2025
5a67bc8
feat(enhanced): alias-aware share-key derivation
ScriptedAlchemy Sep 6, 2025
667a941
chore: debugging
ScriptedAlchemy Sep 8, 2025
ad9b72e
Revert "chore: debugging"
ScriptedAlchemy Sep 8, 2025
2bf170b
Revert "feat(enhanced): alias-aware share-key derivation"
ScriptedAlchemy Sep 8, 2025
4c8ab16
Revert "feat(enhanced): add alias-aware consume matching via resolver…
ScriptedAlchemy Sep 8, 2025
2ec299f
feat(enhanced): add alias-aware providing and consuming for shared mo…
ScriptedAlchemy Sep 8, 2025
baf0d9d
Merge branch 'main' into feat/share-resolver
ScriptedAlchemy Sep 8, 2025
c0810a2
Delete opencode.json
ScriptedAlchemy Sep 8, 2025
097e612
test: add missing aliased target stub for share-with-aliases-provide-…
ScriptedAlchemy Sep 8, 2025
7e502bf
Apply suggested changes
ScriptedAlchemy Sep 8, 2025
f76b386
ci: trigger build
ScriptedAlchemy Sep 9, 2025
7c86fb0
test(enhanced): force sync startup in alias-sharing cases so harness …
ScriptedAlchemy Sep 9, 2025
27e09a9
Merge branch 'main' into feat/share-resolver
ScriptedAlchemy Sep 9, 2025
f15e761
chore: remove unused aliasResolver.ts file
ScriptedAlchemy Sep 9, 2025
e29ec0f
Merge branch 'main' into feat/share-resolver
ScriptedAlchemy Sep 11, 2025
48d4dfd
Delete prompts/alias-resolver.md
ScriptedAlchemy Sep 11, 2025
1c7ead0
refactor(enhanced): resolve alias-aware consumes in afterResolve with…
ScriptedAlchemy Sep 12, 2025
c701d85
test(enhanced): mock afterResolve hook in NMF tests
ScriptedAlchemy Sep 12, 2025
180e9bf
Merge branch 'main' into feat/share-resolver
ScriptedAlchemy Sep 12, 2025
116e775
Merge branch 'feat/share-resolver' into gpt-alias-resolver-refactor
ScriptedAlchemy Sep 12, 2025
095f6aa
fix(enhanced): alias-aware ConsumeSharedPlugin + data URI guard
ScriptedAlchemy Sep 12, 2025
953b8ab
chore: patch
ScriptedAlchemy Sep 19, 2025
341e045
ci(core): enable tag push and npm publish in release workflow
ScriptedAlchemy Sep 19, 2025
5f44b8a
chore: x
ScriptedAlchemy Sep 19, 2025
038ebe8
ci(core): refine release workflow (npm auth, registry, branch fetch)
ScriptedAlchemy Sep 19, 2025
5e0d7e6
chore(core): merge origin/main into gpt-alias-resolver-refactor (theirs)
ScriptedAlchemy Sep 20, 2025
83993f8
fix(enhanced): guard missing hooks in tests (afterResolve, finishModu…
ScriptedAlchemy Sep 20, 2025
202172f
feat(3000-home): add React Query singleton and provider
ScriptedAlchemy Sep 22, 2025
d8609f4
chore(module-federation): ignore tsbuildinfo artifacts
ScriptedAlchemy Sep 22, 2025
d897b6f
Merge remote-tracking branch 'origin/main' into feat/share-resolver
ScriptedAlchemy Sep 22, 2025
0522c50
Merge branch 'feat/share-resolver' of github.com:module-federation/co…
ScriptedAlchemy Sep 22, 2025
78f91a0
Merge branch 'feat/share-resolver' into gpt-alias-resolver-refactor
ScriptedAlchemy Sep 22, 2025
72211ab
refactor: always register finishModules hook
ScriptedAlchemy Sep 22, 2025
d4be790
chore(module-federation): restore release workflow from main
ScriptedAlchemy Sep 22, 2025
65812e4
test(enhanced): mock finishModules hook for ConsumeSharedPlugin.facto…
ScriptedAlchemy Sep 22, 2025
4a6beb0
feat(enhanced): add experiments.aliasConsumption flag; remove env toggle
ScriptedAlchemy Sep 22, 2025
bbeaf3b
fix(enhanced): resolve merge conflicts and semver usage
ScriptedAlchemy Sep 22, 2025
09c7ea1
feat(nextjs-mf): enable experiments.aliasConsumption by default (pres…
ScriptedAlchemy Sep 22, 2025
d1d5eb3
fix(nextjs-mf): adopt allowNodeModulesSuffixMatch and fix types
ScriptedAlchemy Sep 22, 2025
5436969
fix(nextjs-mf): compile errors in share-internals; add tail-key fallb…
ScriptedAlchemy Sep 22, 2025
3d050d6
refactor(enhanced): remove tail-key fallback in aliasConsumption
ScriptedAlchemy Sep 22, 2025
4c215e6
fix(enhanced): suppress singleton warnings for request filters
ScriptedAlchemy Sep 23, 2025
a9bca3d
feat(enhanced): add layer support for exposed modules
ScriptedAlchemy Sep 23, 2025
829e877
fix(nextjs-mf): fix loader chain handling for layer query parameters
ScriptedAlchemy Sep 23, 2025
440fedb
refactor(nextjs-mf): simplify share internals and remove unused code
ScriptedAlchemy Sep 23, 2025
718e27f
fix(nextjs-mf): restore react-query singleton configuration in demo apps
ScriptedAlchemy Sep 23, 2025
608bcea
feat(enhanced): preserve import=false semantics in alias fallback
ScriptedAlchemy Sep 24, 2025
55b92f0
fix(nextjs-mf): restore client share layering
ScriptedAlchemy Sep 24, 2025
88b865d
test: update alias suffix fixtures for enhanced
ScriptedAlchemy Sep 25, 2025
9dba2a0
chore: regenerate container plugin schema
ScriptedAlchemy Sep 25, 2025
8a236a0
test(enhanced): bake stub exports for alias-aware cases
ScriptedAlchemy Sep 25, 2025
e667192
test(enhanced): align provider stubs with esm export shape
ScriptedAlchemy Sep 25, 2025
9394798
chore: commit current work
ScriptedAlchemy Sep 25, 2025
bb91983
chore: align alias provider stub with alias consumption
ScriptedAlchemy Sep 25, 2025
7b2d8c0
chore(sdk): build with tsc and emit sourcemaps
ScriptedAlchemy Sep 25, 2025
6daa3d2
fix: update jest config file extension from .ts to .cjs
ScriptedAlchemy Sep 25, 2025
c79f273
test(enhanced): add cjs jest config for jest-cli
ScriptedAlchemy Sep 25, 2025
7f4d9e0
chore(enhanced): remove redundant ts jest config
ScriptedAlchemy Sep 25, 2025
76df807
chore: align managers snapshot with react 19
ScriptedAlchemy Sep 25, 2025
6b6f81d
chore(core): update package.json and pnpm-lock.yaml
ScriptedAlchemy Sep 25, 2025
f8b51dd
chore(modernjs): force React client entry; drop 'react-server' condit…
ScriptedAlchemy Sep 25, 2025
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
19 changes: 19 additions & 0 deletions .changeset/fix-alias-aware-consume-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@module-federation/enhanced': patch
'@module-federation/nextjs-mf': patch
---

fix(enhanced): ConsumeSharedPlugin alias-aware and virtual resource handling

- Skip `data:` (virtual) resources in `afterResolve` and `createModule` so webpack's scheme resolver handles them (fixes container virtual-entry compile failure)
- Broaden alias-aware matching in `afterResolve` to include deep-path shares that start with the resolved package name (e.g. `next/dist/compiled/react`), ensuring aliased modules are consumed from federation when configured
- Avoid converting explicit relative/absolute requests into consumes to preserve local nested resolution (fixes deep module sharing version selection)
- Keep prefix and node_modules suffix matching intact; no behavior change there
- **NEW**: When no candidates found for framework-compiled packages (e.g., resource under */dist/compiled/*), also check shares matching the original request's package name, enabling pages-dir React shares to work without explicit imports

These changes restore expected behavior for:
- Virtual entry compilation
- Deep module sharing (distinct versions for nested paths)
- Alias-based sharing (Next.js compiled React)
- Pages-dir React shares resolving correctly via aliasConsumption

11 changes: 8 additions & 3 deletions .github/workflows/e2e-next-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,22 @@ jobs:
- name: E2E Test for Next.js Dev - Home
if: steps.check-ci.outcome == 'success'
run: |
killall node
lsof -ti tcp:3000,3001,3002 | xargs -r kill || true
npx nx run 3000-home:test:e2e

- name: E2E Test for Next.js Dev - Shop
if: steps.check-ci.outcome == 'success'
run: |
killall node
lsof -ti tcp:3000,3001,3002 | xargs -r kill || true
npx nx run 3001-shop:test:e2e

- name: E2E Test for Next.js Dev - Checkout
if: steps.check-ci.outcome == 'success'
run: |
killall node
lsof -ti tcp:3000,3001,3002 | xargs -r kill || true
npx nx run 3002-checkout:test:e2e

- name: Teardown (always)
if: always()
run: |
lsof -ti tcp:3000,3001,3002 | xargs -r kill || true
13 changes: 6 additions & 7 deletions .github/workflows/e2e-next-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,9 @@ jobs:
if: steps.check-ci.outcome == 'success'
run: |
pnpm run --filter @module-federation/3002-checkout --filter @module-federation/3000-home --filter @module-federation/3001-shop build &&
pnpm run app:next:prod &
sleep 4 &&
npx wait-on tcp:3001 &&
npx wait-on tcp:3002 &&
npx wait-on tcp:3000 &&
npx nx run-many --target=test:e2e --projects=3000-home,3001-shop,3002-checkout --parallel=1 &&
npx kill-port 3000,3001,3002
npx nx run-many --target=test:e2e --configuration=production --projects=3000-home,3001-shop,3002-checkout --parallel=1

- name: Teardown (always)
if: always()
run: |
npx kill-port 3000,3001,3002 || true
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ apps/**/dist
**/cypress/downloads

# test cases
!packages/enhanced/test/configCases/**/**/node_modules
packages/enhanced/test/js
.ignored
**/.mf
Expand All @@ -77,6 +76,8 @@ packages/enhanced/test/js
# Federation
**/.federation
*.ts.timestamp*
*.tsbuildinfo
**/tsconfig.tsbuildinfo

vite.config.*.timestamp*
vitest.config.*.timestamp*
Expand All @@ -90,4 +91,12 @@ ssg
.claude
# Native binary files
*.node
__mocks__/

# test mock modules
!packages/enhanced/test/configCases/**/**/node_modules
!packages/enhanced/test/configCases/sharing/share-with-aliases/node_modules/next/dist
!packages/enhanced/test/configCases/sharing/share-with-aliases-provide-only/node_modules/next/dist

!packages/enhanced/test/configCases/**/node_modules/**/dist
!packages/enhanced/test/configCases/**/node_modules/**/dist/**
23 changes: 23 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# AGENTS.md - Module Federation Core Repository Guidelines

## Build/Test Commands
```bash
pnpm build # Build all packages (tag:type:pkg)
pnpm test # Run all tests via nx
pnpm lint # Lint all packages
pnpm lint-fix # Fix linting issues
pnpm nx run <pkg>:test # Test specific package
npx jest path/to/test.ts --no-coverage # Run single test file
```

## Code Style
- **Imports**: External → SDK/core → Local (grouped with blank lines)
- **Type imports**: `import type { ... }` explicitly marked
- **Naming**: camelCase functions, PascalCase classes, SCREAMING_SNAKE constants
- **Files**: kebab-case or PascalCase for class files
- **Errors**: Use `@module-federation/error-codes`, minimal try-catch
- **Comments**: Minimal, use `//` inline, `/** */` for deprecation
- **Async**: Named async functions for major ops, arrow functions in callbacks
- **Exports**: Named exports preferred, barrel exports in index files
- **Package manager**: ALWAYS use pnpm, never npm
- **Parallelization**: Break tasks into 3-10 parallel subtasks minimum
25 changes: 25 additions & 0 deletions apps/3000-home/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
import { defineConfig } from 'cypress';

const PORTS_TO_CLEAN = [3000, 3001, 3002] as const;

async function killPorts(ports: readonly number[]) {
try {
const mod = await import('kill-port');
const killPort: (port: number) => Promise<unknown> =
(mod as any).default ?? (mod as any);
for (const p of ports) {
try {
await killPort(p);
} catch {
// ignore if port is not in use
}
}
} catch {
// best-effort cleanup; do not fail the run if kill-port is unavailable
}
}

export default defineConfig({
projectId: 'sa6wfn',
e2e: {
...nxE2EPreset(__filename, { cypressDir: 'cypress' }),
// Please ensure you use `cy.origin()` when navigating between domains and remove this option.
// See https://docs.cypress.io/app/references/migration-guide#Changes-to-cyorigin
injectDocumentDomain: true,
setupNodeEvents(on, config) {
on('after:run', async () => {
await killPorts(PORTS_TO_CLEAN);
});
return config;
},
},
defaultCommandTimeout: 20000,
retries: {
Expand Down
17 changes: 8 additions & 9 deletions apps/3000-home/next.config.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
const { withNx } = require('@nx/next/plugins/with-nx');
const NextFederationPlugin = require('@module-federation/nextjs-mf');

/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
* @type {import('next').NextConfig}
**/
const nextConfig = {
nx: {
// Set this to true if you would like to to use SVGR
// See: https://github.com/gregberge/svgr
svgr: false,
},
webpack(config, options) {
const { isServer } = options;
config.watchOptions = {
Expand Down Expand Up @@ -46,8 +40,13 @@ const nextConfig = {
requiredVersion: '5.19.1',
version: '5.19.1',
},
'@ant-design/': {
'@ant-design/cssinjs': { singleton: true, requiredVersion: false },
// '@ant-design/': { singleton: true },
// Only list real TanStack packages used by this app
'@tanstack/react-query': { singleton: true, requiredVersion: false },
'@tanstack/react-query-devtools': {
singleton: true,
requiredVersion: false,
},
},
extraOptions: {
Expand All @@ -68,4 +67,4 @@ const nextConfig = {
},
};

module.exports = withNx(nextConfig);
module.exports = nextConfig;
6 changes: 4 additions & 2 deletions apps/3000-home/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"@tanstack/react-query": "^5.59.0",
"@tanstack/react-query-devtools": "^5.59.0",
"@ant-design/cssinjs": "^1.21.0",
"antd": "5.19.1",
"lodash": "4.17.21",
"next": "15.3.3",
"react": "19.0.0",
"react-dom": "19.0.0"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@module-federation/nextjs-mf": "workspace:*",
Expand Down
68 changes: 47 additions & 21 deletions apps/3000-home/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,34 @@ import { init } from '@module-federation/runtime';
console.log('logging init', typeof init);
import App from 'next/app';
import { Layout, version, ConfigProvider } from 'antd';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { StyleProvider } from '@ant-design/cssinjs';

import Router, { useRouter } from 'next/router';
const SharedNav = React.lazy(() => import('../components/SharedNav'));
import HostAppMenu from '../components/menu';
function MyApp(props) {
const { Component, pageProps } = props;
// Ensure a single QueryClient instance in the browser; create per-request on SSR
const [queryClient] = React.useState(() => {
if (typeof window === 'undefined') {
return new QueryClient({
defaultOptions: {
queries: { staleTime: 30_000, refetchOnWindowFocus: false },
},
});
}
const w = window as any;
w.__mfQueryClient =
w.__mfQueryClient ||
new QueryClient({
defaultOptions: {
queries: { staleTime: 30_000, refetchOnWindowFocus: false },
},
});
return w.__mfQueryClient as QueryClient;
});
const { asPath } = useRouter();
const [MenuComponent, setMenuComponent] = useState(() => HostAppMenu);
const handleRouteChange = async (url) => {
Expand Down Expand Up @@ -42,30 +63,35 @@ function MyApp(props) {
return (
<StyleProvider layer>
<ConfigProvider theme={{ hashed: false }}>
<Layout style={{ minHeight: '100vh' }} prefixCls={'dd'}>
<React.Suspense>
<SharedNav />
</React.Suspense>
<Layout>
<Layout.Sider width={200}>
<MenuComponent />
</Layout.Sider>
<QueryClientProvider client={queryClient}>
<Layout style={{ minHeight: '100vh' }} prefixCls={'dd'}>
<React.Suspense>
<SharedNav />
</React.Suspense>
<Layout>
<Layout.Content style={{ background: '#fff', padding: 20 }}>
<Component {...pageProps} />
</Layout.Content>
<Layout.Footer
style={{
background: '#fff',
color: '#999',
textAlign: 'center',
}}
>
antd@{version}
</Layout.Footer>
<Layout.Sider width={200}>
<MenuComponent />
</Layout.Sider>
<Layout>
<Layout.Content style={{ background: '#fff', padding: 20 }}>
<Component {...pageProps} />
</Layout.Content>
<Layout.Footer
style={{
background: '#fff',
color: '#999',
textAlign: 'center',
}}
>
antd@{version}
</Layout.Footer>
</Layout>
</Layout>
{process.env.NODE_ENV !== 'production' ? (
<ReactQueryDevtools initialIsOpen={false} />
) : null}
</Layout>
</Layout>
</QueryClientProvider>
</ConfigProvider>
</StyleProvider>
);
Expand Down
49 changes: 9 additions & 40 deletions apps/3000-home/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,24 @@
"tags": [],
"targets": {
"build": {
"executor": "@nx/next:build",
"defaultConfiguration": "production",
"executor": "nx:run-commands",
"options": {
"outputPath": "apps/3000-home"
},
"configurations": {
"development": {
"outputPath": "apps/3000-home"
},
"production": {}
},
"dependsOn": [
{
"target": "build",
"dependencies": true
}
]
"command": "next build",
"cwd": "apps/3000-home"
}
},
"serve": {
"executor": "@nx/next:server",
"executor": "nx:run-commands",
"defaultConfiguration": "development",
"options": {
"buildTarget": "3000-home:build",
"dev": true,
"port": 3000
"command": "next dev -p 3000",
"cwd": "apps/3000-home"
},
"configurations": {
"development": {
"buildTarget": "3000-home:build:development",
"dev": true,
"port": 3000
},
"production": {
"buildTarget": "3000-home:build:production",
"dev": false,
"port": 3000
"command": "next start -p 3000",
"cwd": "apps/3000-home"
}
},
"dependsOn": [
{
"target": "build",
"dependencies": true
}
]
},
"export": {
"executor": "@nx/next:export",
"options": {
"buildTarget": "3000-home:build:production"
}
},
"lint": {
Expand Down
Loading
Loading