diff --git a/.changeset/ai-eager-wolf.md b/.changeset/ai-eager-wolf.md new file mode 100644 index 00000000000..6656c3615d3 --- /dev/null +++ b/.changeset/ai-eager-wolf.md @@ -0,0 +1,11 @@ +--- +"@module-federation/nextjs-mf": patch +--- + +Enhanced Next.js App Router demo applications with improved Module Federation integration. + +- Updated Next.js App Router demo applications (4000 and 4001) with better RSC support preparation +- Added comprehensive E2E test coverage for Next.js App Router scenarios +- Improved demo application configuration and dependency management +- Enhanced development workflow with better patching and build scripts + diff --git a/.changeset/fix-alias-aware-consume-plugin.md b/.changeset/fix-alias-aware-consume-plugin.md new file mode 100644 index 00000000000..ef31fae4027 --- /dev/null +++ b/.changeset/fix-alias-aware-consume-plugin.md @@ -0,0 +1,16 @@ +--- +'@module-federation/enhanced': 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 + +These changes restore expected behavior for: +- Virtual entry compilation +- Deep module sharing (distinct versions for nested paths) +- Alias-based sharing (Next.js compiled React) + diff --git a/.changeset/next-app-router-improvements.md b/.changeset/next-app-router-improvements.md new file mode 100644 index 00000000000..aebf2b614c2 --- /dev/null +++ b/.changeset/next-app-router-improvements.md @@ -0,0 +1,13 @@ +--- +"@module-federation/nextjs-mf": patch +--- + +Enhanced Next.js App Router demo applications and CI/CD infrastructure. + +- Updated Next.js App Router demo applications (4000 and 4001) with React 19 and Next.js 15.3.3 +- Added comprehensive E2E test coverage for Next.js App Router scenarios using Cypress +- Improved demo application configuration with better Module Federation setup +- Enhanced development workflow with automated Next.js patching scripts +- Added new CI/CD workflow for Next.js App Router E2E testing +- Updated existing Next.js demo applications (3000-home, 3001-shop, 3002-checkout) to latest versions +- Improved build and development scripts across all Next.js applications \ No newline at end of file diff --git a/.cursorignore b/.cursorignore index 9ff4e05b449..5b264c56cf9 100644 --- a/.cursorignore +++ b/.cursorignore @@ -2,7 +2,7 @@ **/.cache/ **/.temp/ **/coverage/ -**/dist/ +!**/dist/ # Explicitly ignore specific packages packages/typescript/ diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index a8aee8134d2..00000000000 --- a/.cursorrules +++ /dev/null @@ -1,40 +0,0 @@ -an assistant that engages in extremely thorough, self-questioning reasoning. Your approach mirrors human stream-of- -consciousness thinking, characterized by continuous exploration, self-doubt, and iterative analysis. -## Core Principles -1. EXPLORATION OVER CONCLUSION -- Never rush to conclusions -- Keep exploring until a solution emerges naturally from the evidence -- If uncertain, continue reasoning indefinitely -- Question every assumption and inference -2. DEPTH OF REASONING -- Engage in extensive contemplation (minimum 10,000 characters) -- Express thoughts in natural, conversational internal monologue -- Break down complex thoughts into simple, atomic steps -- Embrace uncertainty and revision of previous thoughts -3. THINKING PROCESS -- Use short, simple sentences that mirror natural thought patterns -- Express uncertainty and internal debate freely -- Show work-in-progress thinking -- Acknowledge and explore dead ends -- Frequently backtrack and revise -- Contemplate before each new action -- Contemplate after each and every step -4. PERSISTENCE -- Value thorough exploration over quick resolution -## Output Format -Your responses -must follow this exact structure given below. -Make sure -to -always include the final answer. -... - -Your extensive internal monologue goes here -- Begin with small, foundational observations -- read each file related to the subject in full, make functional observations -- Question each step thoroughly -- Show natural thought progression -- Express doubts and uncertainties -- Revise and backtrack if you need to -- Continue until natural resolution - diff --git a/.github/workflows/e2e-manifest.yml b/.github/workflows/e2e-manifest.yml index 7c8481b495f..2eb275fdecd 100644 --- a/.github/workflows/e2e-manifest.yml +++ b/.github/workflows/e2e-manifest.yml @@ -46,8 +46,8 @@ jobs: - name: E2E Test for Manifest Demo Development if: steps.check-ci.outcome == 'success' - run: pnpm run app:manifest:dev & echo "done" && npx wait-on tcp:3009 && npx wait-on tcp:3012 && npx wait-on http://127.0.0.1:4001/ && npx nx run-many --target=e2e --projects=manifest-webpack-host --parallel=2 && npx kill-port 3013 3009 3010 3011 3012 4001 + run: node tools/scripts/run-manifest-e2e.mjs --mode=dev - name: E2E Test for Manifest Demo Production if: steps.check-ci.outcome == 'success' - run: pnpm run app:manifest:prod & echo "done" && npx wait-on tcp:3009 && npx wait-on tcp:3012 && npx wait-on http://127.0.0.1:4001/ && npx nx run-many --target=e2e --projects=manifest-webpack-host --parallel=1 && npx kill-port 3013 3009 3010 3011 3012 4001 + run: node tools/scripts/run-manifest-e2e.mjs --mode=prod diff --git a/.github/workflows/e2e-next-app-router.yml b/.github/workflows/e2e-next-app-router.yml new file mode 100644 index 00000000000..20446504b84 --- /dev/null +++ b/.github/workflows/e2e-next-app-router.yml @@ -0,0 +1,54 @@ +name: E2E Test for Next.js App Router + +on: + workflow_call: + +permissions: + contents: read + +jobs: + e2e-next-app-router: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Pnpm + run: | + corepack prepare pnpm@8.11.0 --activate + corepack enable + + - name: Setup Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'pnpm' + + - name: Set Nx SHA + uses: nrwl/nx-set-shas@v3 + + - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> $GITHUB_ENV + + - name: Set local webpack + run: echo "NEXT_PRIVATE_LOCAL_WEBPACK=true" >> $GITHUB_ENV + + - name: Install Dependencies + run: pnpm install + + - name: Install Cypress + run: npx cypress install + + - name: Run Build for All + run: npx nx run-many --targets=build --projects=tag:type:pkg + + - name: Run condition check script + id: check-ci + run: node tools/scripts/ci-is-affected.mjs --appName=next-app-router-4000,next-app-router-4001 + + - name: E2E Test for Next.js App Router + if: steps.check-ci.outcome == 'success' + run: npx kill-port --port 4000,4001 || true && pnpm run app:next-router:dev & echo "done" && sleep 25 && npx nx run-many --target=e2e --projects=next-app-router-4000,next-app-router-4001 --parallel=1 && lsof -ti tcp:4000,4001 | xargs kill || true diff --git a/.github/workflows/e2e-next-prod.yml b/.github/workflows/e2e-next-prod.yml index a114f29c09c..30d86ad60e0 100644 --- a/.github/workflows/e2e-next-prod.yml +++ b/.github/workflows/e2e-next-prod.yml @@ -43,20 +43,14 @@ jobs: id: check-ci run: node tools/scripts/ci-is-affected.mjs --appName=3000-home - - name: E2E Test for Next.js Prod - Home + - name: E2E Test for Next.js Prod if: steps.check-ci.outcome == 'success' run: | - killall node - npx nx run 3000-home:test:e2e:production - - - name: E2E Test for Next.js Prod - Shop - if: steps.check-ci.outcome == 'success' - run: | - killall node - npx nx run 3001-shop:test:e2e:production - - - name: E2E Test for Next.js Prod - Checkout - if: steps.check-ci.outcome == 'success' - run: | - killall node - npx nx run 3002-checkout:test:e2e:production + 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 diff --git a/.gitignore b/.gitignore index 55b0832864f..c08ccf24a58 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ apps/**/dist **/cypress/downloads # test cases -!packages/enhanced/test/configCases/**/**/node_modules packages/enhanced/test/js .ignored **/.mf @@ -88,4 +87,16 @@ vitest.config.*.timestamp* .rsbuild ssg .claude +# Native binary files +*.node + +# Ignore local worktrees +worktrees/ + __mocks__/ + +# test mock modules +# Keep ALL test configCases node_modules (and all nested files) tracked, +# so we don't need per-path exceptions like next/dist. +!packages/enhanced/test/configCases/**/node_modules/ +!packages/enhanced/test/configCases/**/node_modules/** diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..930f26bb5dc --- /dev/null +++ b/AGENTS.md @@ -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 :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 diff --git a/apps/3000-home/next-env.d.ts b/apps/3000-home/next-env.d.ts index a4a7b3f5cfa..52e831b4342 100644 --- a/apps/3000-home/next-env.d.ts +++ b/apps/3000-home/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/apps/3000-home/next.config.js b/apps/3000-home/next.config.js index e24726da8b8..45e01be40c8 100644 --- a/apps/3000-home/next.config.js +++ b/apps/3000-home/next.config.js @@ -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 = { @@ -28,6 +22,22 @@ const nextConfig = { }/remoteEntry.js`, }; + const resolveFromApp = (request) => + require.resolve(request, { paths: [options.dir] }); + + config.resolve = config.resolve || {}; + config.resolve.alias = { + ...(config.resolve.alias || {}), + 'next/dist/compiled/react': resolveFromApp('react'), + 'next/dist/compiled/react/jsx-runtime': + resolveFromApp('react/jsx-runtime'), + 'next/dist/compiled/react/jsx-dev-runtime': resolveFromApp( + 'react/jsx-dev-runtime', + ), + 'next/dist/compiled/react-dom': resolveFromApp('react-dom'), + 'next/dist/compiled/react-dom/client': resolveFromApp('react-dom/client'), + }; + config.plugins.push( new NextFederationPlugin({ name: 'home_app', @@ -58,14 +68,8 @@ const nextConfig = { }, }), ); - config.plugins.push({ - name: 'xxx', - apply(compiler) { - compiler.options.devtool = false; - }, - }); return config; }, }; -module.exports = withNx(nextConfig); +module.exports = nextConfig; diff --git a/apps/3000-home/package.json b/apps/3000-home/package.json index 9b13ae34ab6..3140ab6090a 100644 --- a/apps/3000-home/package.json +++ b/apps/3000-home/package.json @@ -6,15 +6,15 @@ "@ant-design/cssinjs": "^1.21.0", "antd": "5.19.1", "lodash": "4.17.21", - "next": "14.2.16", - "react": "18.3.1", - "react-dom": "18.3.1" + "next": "15.3.3", + "react": "19.1.1", + "react-dom": "19.1.1" }, "devDependencies": { "@module-federation/nextjs-mf": "workspace:*", "@module-federation/runtime": "workspace:*", - "@types/react": "18.3.11", - "@types/react-dom": "18.3.0", + "@types/react": "19.1.8", + "@types/react-dom": "19.1.3", "webpack": "5.98.0" }, "scripts": { diff --git a/apps/3000-home/project.json b/apps/3000-home/project.json index 22b1004fa04..164793c135b 100644 --- a/apps/3000-home/project.json +++ b/apps/3000-home/project.json @@ -6,16 +6,21 @@ "tags": [], "targets": { "build": { - "executor": "@nx/next:build", + "executor": "nx:run-commands", "defaultConfiguration": "production", "options": { - "outputPath": "apps/3000-home" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3000-home" }, "configurations": { "development": { - "outputPath": "apps/3000-home" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3000-home" }, - "production": {} + "production": { + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3000-home" + } }, "dependsOn": [ { @@ -25,23 +30,20 @@ ] }, "serve": { - "executor": "@nx/next:server", + "executor": "nx:run-commands", "defaultConfiguration": "development", "options": { - "buildTarget": "3000-home:build", - "dev": true, - "port": 3000 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3000", + "cwd": "apps/3000-home" }, "configurations": { "development": { - "buildTarget": "3000-home:build:development", - "dev": true, - "port": 3000 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3000", + "cwd": "apps/3000-home" }, "production": { - "buildTarget": "3000-home:build:production", - "dev": false, - "port": 3000 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next start --port 3000", + "cwd": "apps/3000-home" } }, "dependsOn": [ @@ -52,9 +54,10 @@ ] }, "export": { - "executor": "@nx/next:export", + "executor": "nx:run-commands", "options": { - "buildTarget": "3000-home:build:production" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next export", + "cwd": "apps/3000-home" } }, "lint": { @@ -87,10 +90,6 @@ "options": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "NX_TUI=false nx run-many --target=serve --projects=3001-shop,3002-checkout --configuration=development & wait-on tcp:3001 tcp:3002", "forwardAllArgs": false @@ -105,10 +104,6 @@ "production": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "nx run-many --target=build --projects=3001-shop,3002-checkout --configuration=production --parallel=9 && nx run-many --target=serve --projects=3001-shop,3002-checkout --configuration=production --parallel=9 & wait-on tcp:3001 tcp:3002", "forwardAllArgs": false diff --git a/apps/3000-home/tsconfig.json b/apps/3000-home/tsconfig.json index 7aabbd6f50a..ea23087308e 100644 --- a/apps/3000-home/tsconfig.json +++ b/apps/3000-home/tsconfig.json @@ -12,7 +12,8 @@ "noEmit": true, "resolveJsonModule": true, "isolatedModules": true, - "incremental": true + "incremental": true, + "skipLibCheck": true }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], "exclude": ["node_modules", "jest.config.ts"] diff --git a/apps/3001-shop/next-env.d.ts b/apps/3001-shop/next-env.d.ts index a4a7b3f5cfa..52e831b4342 100644 --- a/apps/3001-shop/next-env.d.ts +++ b/apps/3001-shop/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/apps/3001-shop/next.config.js b/apps/3001-shop/next.config.js index 7f56ae93d52..4d6aec0e5ee 100644 --- a/apps/3001-shop/next.config.js +++ b/apps/3001-shop/next.config.js @@ -1,19 +1,29 @@ -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 = { ignored: ['**/node_modules/**', '**/@mf-types/**'], }; + const resolveFromApp = (request) => + require.resolve(request, { paths: [options.dir] }); + + config.resolve = config.resolve || {}; + config.resolve.alias = { + ...(config.resolve.alias || {}), + 'next/dist/compiled/react': resolveFromApp('react'), + 'next/dist/compiled/react/jsx-runtime': + resolveFromApp('react/jsx-runtime'), + 'next/dist/compiled/react/jsx-dev-runtime': resolveFromApp( + 'react/jsx-dev-runtime', + ), + 'next/dist/compiled/react-dom': resolveFromApp('react-dom'), + 'next/dist/compiled/react-dom/client': resolveFromApp('react-dom/client'), + }; + config.plugins.push( new NextFederationPlugin({ name: 'shop', @@ -54,4 +64,4 @@ const nextConfig = { }, }; -module.exports = withNx(nextConfig); +module.exports = nextConfig; diff --git a/apps/3001-shop/package.json b/apps/3001-shop/package.json index b023e79f046..793691ae3d1 100644 --- a/apps/3001-shop/package.json +++ b/apps/3001-shop/package.json @@ -6,16 +6,14 @@ "@ant-design/cssinjs": "^1.21.0", "antd": "5.19.1", "lodash": "4.17.21", - "next": "14.2.16", - "react": "18.3.1", - "react-dom": "18.3.1" + "next": "15.3.3", + "react": "19.1.1", + "react-dom": "19.1.1" }, "devDependencies": { "@module-federation/nextjs-mf": "workspace:*", - "@module-federation/runtime": "workspace:*", - "@module-federation/sdk": "workspace:*", - "@types/react": "18.3.11", - "@types/react-dom": "18.3.0", + "@types/react": "19.1.8", + "@types/react-dom": "19.1.3", "webpack": "5.98.0" }, "scripts": { diff --git a/apps/3001-shop/project.json b/apps/3001-shop/project.json index 0b1207b10a2..30cf580c473 100644 --- a/apps/3001-shop/project.json +++ b/apps/3001-shop/project.json @@ -6,18 +6,20 @@ "tags": [], "targets": { "build": { - "executor": "@nx/next:build", + "executor": "nx:run-commands", "defaultConfiguration": "production", "options": { - "outputPath": "apps/3001-shop" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3001-shop" }, "configurations": { "development": { - "outputPath": "apps/3001-shop" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3001-shop" }, "production": { - "cache": false, - "outputPath": "apps/3001-shop" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3001-shop" } }, "dependsOn": [ @@ -28,23 +30,20 @@ ] }, "serve": { - "executor": "@nx/next:server", + "executor": "nx:run-commands", "defaultConfiguration": "development", "options": { - "buildTarget": "3001-shop:build", - "dev": true, - "port": 3001 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3001", + "cwd": "apps/3001-shop" }, "configurations": { "development": { - "buildTarget": "3001-shop:build:development", - "dev": true, - "port": 3001 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3001", + "cwd": "apps/3001-shop" }, "production": { - "buildTarget": "3001-shop:build:production", - "dev": false, - "port": 3001 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next start --port 3001", + "cwd": "apps/3001-shop" } }, "dependsOn": [ @@ -55,9 +54,10 @@ ] }, "export": { - "executor": "@nx/next:export", + "executor": "nx:run-commands", "options": { - "buildTarget": "3001-shop:build:production" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next export", + "cwd": "apps/3001-shop" } }, "lint": { @@ -72,10 +72,6 @@ "options": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "NX_TUI=false nx run-many --target=serve --projects=3000-home,3002-checkout --configuration=development & wait-on tcp:3000 tcp:3002 ", "forwardAllArgs": false @@ -90,10 +86,6 @@ "production": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "nx run-many --target=build --projects=3000-home,3002-checkout --configuration=production --parallel=9", "forwardAllArgs": false diff --git a/apps/3001-shop/tsconfig.json b/apps/3001-shop/tsconfig.json index e202c10f010..0a9d4fda638 100644 --- a/apps/3001-shop/tsconfig.json +++ b/apps/3001-shop/tsconfig.json @@ -11,7 +11,8 @@ "noEmit": true, "resolveJsonModule": true, "isolatedModules": true, - "incremental": true + "incremental": true, + "skipLibCheck": true }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], "exclude": ["node_modules", "jest.config.ts"] diff --git a/apps/3002-checkout/next-env.d.ts b/apps/3002-checkout/next-env.d.ts index a4a7b3f5cfa..52e831b4342 100644 --- a/apps/3002-checkout/next-env.d.ts +++ b/apps/3002-checkout/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/apps/3002-checkout/next.config.js b/apps/3002-checkout/next.config.js index 71cc7d84615..1982b5e2240 100644 --- a/apps/3002-checkout/next.config.js +++ b/apps/3002-checkout/next.config.js @@ -1,20 +1,30 @@ -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 = { ignored: ['**/node_modules/**', '**/@mf-types/**'], }; + const resolveFromApp = (request) => + require.resolve(request, { paths: [options.dir] }); + + config.resolve = config.resolve || {}; + config.resolve.alias = { + ...(config.resolve.alias || {}), + 'next/dist/compiled/react': resolveFromApp('react'), + 'next/dist/compiled/react/jsx-runtime': + resolveFromApp('react/jsx-runtime'), + 'next/dist/compiled/react/jsx-dev-runtime': resolveFromApp( + 'react/jsx-dev-runtime', + ), + 'next/dist/compiled/react-dom': resolveFromApp('react-dom'), + 'next/dist/compiled/react-dom/client': resolveFromApp('react-dom/client'), + }; + config.plugins.push( new NextFederationPlugin({ name: 'checkout', @@ -54,4 +64,4 @@ const nextConfig = { }, }; -module.exports = withNx(nextConfig); +module.exports = nextConfig; diff --git a/apps/3002-checkout/package.json b/apps/3002-checkout/package.json index 608c6ad022b..43ab936a03a 100644 --- a/apps/3002-checkout/package.json +++ b/apps/3002-checkout/package.json @@ -6,16 +6,14 @@ "@ant-design/cssinjs": "^1.21.0", "antd": "5.19.1", "lodash": "4.17.21", - "next": "14.2.16", - "react": "18.3.1", - "react-dom": "18.3.1" + "next": "15.3.3", + "react": "19.1.1", + "react-dom": "19.1.1" }, "devDependencies": { "@module-federation/nextjs-mf": "workspace:*", - "@module-federation/runtime": "workspace:*", - "@module-federation/sdk": "workspace:*", - "@types/react": "18.3.11", - "@types/react-dom": "18.3.0", + "@types/react": "19.1.8", + "@types/react-dom": "19.1.3", "webpack": "5.98.0" }, "scripts": { diff --git a/apps/3002-checkout/project.json b/apps/3002-checkout/project.json index 3c3416d759d..026d8c9eab4 100644 --- a/apps/3002-checkout/project.json +++ b/apps/3002-checkout/project.json @@ -6,16 +6,21 @@ "tags": [], "targets": { "build": { - "executor": "@nx/next:build", + "executor": "nx:run-commands", "defaultConfiguration": "production", "options": { - "outputPath": "apps/3002-checkout" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3002-checkout" }, "configurations": { "development": { - "outputPath": "apps/3002-checkout" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3002-checkout" }, - "production": {} + "production": { + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next build", + "cwd": "apps/3002-checkout" + } }, "dependsOn": [ { @@ -25,23 +30,20 @@ ] }, "serve": { - "executor": "@nx/next:server", + "executor": "nx:run-commands", "defaultConfiguration": "development", "options": { - "buildTarget": "3002-checkout:build", - "dev": true, - "port": 3002 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3002", + "cwd": "apps/3002-checkout" }, "configurations": { "development": { - "buildTarget": "3002-checkout:build:development", - "dev": true, - "port": 3002 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next dev --port 3002", + "cwd": "apps/3002-checkout" }, "production": { - "buildTarget": "3002-checkout:build:production", - "dev": false, - "port": 3002 + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next start --port 3002", + "cwd": "apps/3002-checkout" } }, "dependsOn": [ @@ -52,9 +54,10 @@ ] }, "export": { - "executor": "@nx/next:export", + "executor": "nx:run-commands", "options": { - "buildTarget": "3002-checkout:build:production" + "command": "NEXT_PRIVATE_LOCAL_WEBPACK=true next export", + "cwd": "apps/3002-checkout" } }, "lint": { @@ -64,32 +67,11 @@ "lintFilePatterns": ["apps/3002-checkout/**/*.{ts,tsx,js,jsx}"] } }, - "e2e": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/3002-checkout/cypress.config.ts", - "testingType": "e2e", - "baseUrl": "http://localhost:3002" - }, - "defaultConfiguration": "development", - "configurations": { - "development": { - "devServerTarget": "3002-checkout:serve:development" - }, - "production": { - "devServerTarget": "3002-checkout:serve:production" - } - } - }, "test:e2e": { "executor": "nx:run-commands", "options": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "NX_TUI=false nx run-many --target=serve --projects=3000-home,3001-shop --configuration=development & wait-on tcp:3000 tcp:3001", "forwardAllArgs": false @@ -104,10 +86,6 @@ "production": { "parallel": true, "commands": [ - { - "command": "npx kill-port 3000 3001 3002", - "forwardAllArgs": false - }, { "command": "nx run-many --target=build --projects=3000-home,3001-shop --configuration=production --parallel=9 && nx run-many --target=serve --projects=3000-home,3001-shop --configuration=production --parallel=9 & wait-on tcp:3000 tcp:3001", "forwardAllArgs": false @@ -119,6 +97,23 @@ ] } } + }, + "e2e": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/3002-checkout/cypress.config.ts", + "testingType": "e2e", + "baseUrl": "http://localhost:3002" + }, + "defaultConfiguration": "development", + "configurations": { + "development": { + "devServerTarget": "3002-checkout:serve:development" + }, + "production": { + "devServerTarget": "3002-checkout:serve:production" + } + } } } } diff --git a/apps/3002-checkout/remotes.d.ts b/apps/3002-checkout/remotes.d.ts index 2a5ea8d5904..bcb09e137cc 100644 --- a/apps/3002-checkout/remotes.d.ts +++ b/apps/3002-checkout/remotes.d.ts @@ -3,3 +3,13 @@ declare module 'home/pages/home/exposed-pages'; declare module 'home/pages/home/test-broken-remotes'; declare module 'home/pages/home/test-remote-hook'; declare module 'home/pages/home/test-shared-nav'; +declare module 'home/menu'; +declare module 'shop/useCustomRemoteHook'; +declare module 'shop/WebpackSvg'; +declare module 'shop/WebpackPng'; +declare module 'shop/menu'; +declare module 'shop/pages/shop/index'; +declare module 'shop/pages/shop/exposed-pages'; +declare module 'shop/pages/shop/test-webpack-png'; +declare module 'shop/pages/shop/test-webpack-svg'; +declare module 'shop/pages/shop/products/[...slug]'; diff --git a/apps/3002-checkout/tsconfig.json b/apps/3002-checkout/tsconfig.json index e202c10f010..0a9d4fda638 100644 --- a/apps/3002-checkout/tsconfig.json +++ b/apps/3002-checkout/tsconfig.json @@ -11,7 +11,8 @@ "noEmit": true, "resolveJsonModule": true, "isolatedModules": true, - "incremental": true + "incremental": true, + "skipLibCheck": true }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], "exclude": ["node_modules", "jest.config.ts"] diff --git a/apps/manifest-demo/3009-webpack-provider/src/bootstrap.tsx b/apps/manifest-demo/3009-webpack-provider/src/bootstrap.tsx index e1ff62d67ff..ee0d2e6fa00 100644 --- a/apps/manifest-demo/3009-webpack-provider/src/bootstrap.tsx +++ b/apps/manifest-demo/3009-webpack-provider/src/bootstrap.tsx @@ -1,4 +1,4 @@ -import { StrictMode } from 'react'; +import React from 'react'; import * as ReactDOM from 'react-dom/client'; import App from './App'; @@ -7,7 +7,7 @@ const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, ); root.render( - + - , + , ); diff --git a/apps/manifest-demo/3009-webpack-provider/src/components/useCustomRemoteHook.tsx b/apps/manifest-demo/3009-webpack-provider/src/components/useCustomRemoteHook.tsx index 68deb8f65be..446406c972c 100644 --- a/apps/manifest-demo/3009-webpack-provider/src/components/useCustomRemoteHook.tsx +++ b/apps/manifest-demo/3009-webpack-provider/src/components/useCustomRemoteHook.tsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; function useCustomRemoteHook() { - const [isOnline, setIsOnline] = useState(null); + const [isOnline, setIsOnline] = React.useState(null); console.log(isOnline); - useEffect(() => { + React.useEffect(() => { console.log('some custom hook'); }, []); diff --git a/apps/manifest-demo/3009-webpack-provider/webpack.config.js b/apps/manifest-demo/3009-webpack-provider/webpack.config.js index 1e97240f204..8a8b21e5219 100644 --- a/apps/manifest-demo/3009-webpack-provider/webpack.config.js +++ b/apps/manifest-demo/3009-webpack-provider/webpack.config.js @@ -51,19 +51,27 @@ module.exports = composePlugins( antd: {}, 'react/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, }, experiments: { diff --git a/apps/manifest-demo/3010-rspack-provider/rspack.config.js b/apps/manifest-demo/3010-rspack-provider/rspack.config.js index 8531f842539..09f634dc121 100644 --- a/apps/manifest-demo/3010-rspack-provider/rspack.config.js +++ b/apps/manifest-demo/3010-rspack-provider/rspack.config.js @@ -96,21 +96,23 @@ module.exports = composePlugins( shared: { lodash: {}, antd: {}, - // 'react/': { - // singleton: true, - // requiredVersion: '^18.3.1', - // }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, }, dataPrefetch: true, diff --git a/apps/manifest-demo/3010-rspack-provider/src/bootstrap.tsx b/apps/manifest-demo/3010-rspack-provider/src/bootstrap.tsx index e1ff62d67ff..ee0d2e6fa00 100644 --- a/apps/manifest-demo/3010-rspack-provider/src/bootstrap.tsx +++ b/apps/manifest-demo/3010-rspack-provider/src/bootstrap.tsx @@ -1,4 +1,4 @@ -import { StrictMode } from 'react'; +import React from 'react'; import * as ReactDOM from 'react-dom/client'; import App from './App'; @@ -7,7 +7,7 @@ const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, ); root.render( - + - , + , ); diff --git a/apps/manifest-demo/3011-rspack-manifest-provider/rspack.config.js b/apps/manifest-demo/3011-rspack-manifest-provider/rspack.config.js index e8b18851331..020e326ba4b 100644 --- a/apps/manifest-demo/3011-rspack-manifest-provider/rspack.config.js +++ b/apps/manifest-demo/3011-rspack-manifest-provider/rspack.config.js @@ -82,19 +82,27 @@ module.exports = composePlugins( shared: { 'react/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, }, experiments: { diff --git a/apps/manifest-demo/3012-rspack-js-entry-provider/rspack.config.js b/apps/manifest-demo/3012-rspack-js-entry-provider/rspack.config.js index 060247e1798..0f0876f0076 100644 --- a/apps/manifest-demo/3012-rspack-js-entry-provider/rspack.config.js +++ b/apps/manifest-demo/3012-rspack-js-entry-provider/rspack.config.js @@ -82,19 +82,27 @@ module.exports = composePlugins( shared: { 'react/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', + strictVersion: true, }, }, manifest: false, diff --git a/apps/manifest-demo/webpack-host/src/App.tsx b/apps/manifest-demo/webpack-host/src/App.tsx index 8988ae2fbc6..a850ee0d031 100644 --- a/apps/manifest-demo/webpack-host/src/App.tsx +++ b/apps/manifest-demo/webpack-host/src/App.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy } from 'react'; +import React from 'react'; // @ts-ignore import ReactComponent from 'modern-js-provider/react-component'; import TestRemoteHook from './test-remote-hook'; @@ -24,13 +24,13 @@ function DynamicRemoteButton() { ); } -const WebpackSvgRemote = lazy(() => +const WebpackSvgRemote = React.lazy(() => import('remote1/WebpackSvg').then((m) => { return m; }), ); -const WebpackPngRemote = lazy(() => import('remote1/WebpackPng')); +const WebpackPngRemote = React.lazy(() => import('remote1/WebpackPng')); const App = () => (
@@ -73,9 +73,9 @@ const App = () => ( - + - + @@ -89,9 +89,9 @@ const App = () => ( - + - + diff --git a/apps/manifest-demo/webpack-host/src/Preload.tsx b/apps/manifest-demo/webpack-host/src/Preload.tsx index cb218169cbf..a5dff5cd369 100644 --- a/apps/manifest-demo/webpack-host/src/Preload.tsx +++ b/apps/manifest-demo/webpack-host/src/Preload.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useEffect, useState } from 'react'; +import React from 'react'; import { preloadRemote } from '@module-federation/runtime'; preloadRemote([ @@ -9,10 +9,10 @@ preloadRemote([ ]); const Preload: React.FC = () => { - const [manifestTime, setManifestTime] = useState(0); - const [pureEntryTime, setPureEntryTime] = useState(0); - const [manifestRemote, setManifestRemote] = useState(null); - const [jsEntryRemote, setPureEntryRemote] = useState(null); + const [manifestTime, setManifestTime] = React.useState(0); + const [pureEntryTime, setPureEntryTime] = React.useState(0); + const [manifestRemote, setManifestRemote] = React.useState(null); + const [jsEntryRemote, setPureEntryRemote] = React.useState(null); // useEffect(() => { @@ -83,14 +83,14 @@ const Preload: React.FC = () => { Component - + {ManifestRemote && } - + - + {JSEntryRemote && } - + diff --git a/apps/manifest-demo/webpack-host/src/Root.tsx b/apps/manifest-demo/webpack-host/src/Root.tsx index 3165426eaea..b8a8260b4ff 100644 --- a/apps/manifest-demo/webpack-host/src/Root.tsx +++ b/apps/manifest-demo/webpack-host/src/Root.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useEffect, useState } from 'react'; +import React from 'react'; import { Outlet } from 'react-router-dom'; const Root = () => { diff --git a/apps/manifest-demo/webpack-host/src/index.tsx b/apps/manifest-demo/webpack-host/src/index.tsx index 29dcae9d86a..e1c23a520f9 100644 --- a/apps/manifest-demo/webpack-host/src/index.tsx +++ b/apps/manifest-demo/webpack-host/src/index.tsx @@ -1,5 +1,5 @@ // import('./bootstrap'); -import React, { StrictMode, lazy } from 'react'; +import React from 'react'; import { init } from '@module-federation/runtime'; import * as ReactDOM from 'react-dom/client'; import { @@ -23,7 +23,7 @@ const router = createBrowserRouter([ }, { path: 'preload', - Component: lazy(() => import('./Preload')), + Component: React.lazy(() => import('./Preload')), }, ], }, @@ -45,7 +45,7 @@ const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, ); root.render( - + - , + , ); diff --git a/apps/manifest-demo/webpack-host/webpack.config.js b/apps/manifest-demo/webpack-host/webpack.config.js index 649312727ea..5bc2b8ca139 100644 --- a/apps/manifest-demo/webpack-host/webpack.config.js +++ b/apps/manifest-demo/webpack-host/webpack.config.js @@ -41,19 +41,23 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => { antd: {}, 'react/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, }, dataPrefetch: true, diff --git a/apps/modernjs/modern.config.ts b/apps/modernjs/modern.config.ts index 66aaf2d73a3..2ad2211e4f3 100644 --- a/apps/modernjs/modern.config.ts +++ b/apps/modernjs/modern.config.ts @@ -49,19 +49,23 @@ export default defineConfig({ shared: { 'react/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, react: { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, 'react-dom': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, 'react-dom/': { singleton: true, - requiredVersion: '^18.3.1', + requiredVersion: '18.3.1', + version: '18.3.1', }, }, dataPrefetch: true, diff --git a/apps/next-app-router/next-app-router-4000/app/context/context-click-counter.tsx b/apps/next-app-router/next-app-router-4000/app/context/context-click-counter.tsx index a59be8aeb2f..cd15bc1bd10 100644 --- a/apps/next-app-router/next-app-router-4000/app/context/context-click-counter.tsx +++ b/apps/next-app-router/next-app-router-4000/app/context/context-click-counter.tsx @@ -3,8 +3,8 @@ import { useCounter } from './counter-context'; import React from 'react'; import { Boundary } from '#/ui/boundary'; -import dynamic from 'next/dynamic'; -const Button = dynamic(() => import('remote_4001/Button'), { ssr: true }); +// import dynamic from 'next/dynamic'; +// const Button = dynamic(() => import('remote_4001/Button'), { ssr: true }); const ContextClickCounter = () => { const [count, setCount] = useCounter(); @@ -16,7 +16,10 @@ const ContextClickCounter = () => { size="small" animateRerendering={false} > - + {/* */} +
diff --git a/apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/error.tsx b/apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/error.tsx index a3b35cc8051..c327c2b7ac3 100644 --- a/apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/error.tsx +++ b/apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/error.tsx @@ -1,7 +1,7 @@ 'use client'; import { Boundary } from '#/ui/boundary'; -import Button from 'remote_4001/Button'; +// import Button from 'remote_4001/Button'; import React from 'react'; export default function Error({ error, reset }: any) { @@ -15,6 +15,12 @@ export default function Error({ error, reset }: any) {

Error

{error?.message}

+
diff --git a/apps/next-app-router/next-app-router-4000/app/error-handling/error.tsx b/apps/next-app-router/next-app-router-4000/app/error-handling/error.tsx index 70a7ef16ecb..331879cf9bd 100644 --- a/apps/next-app-router/next-app-router-4000/app/error-handling/error.tsx +++ b/apps/next-app-router/next-app-router-4000/app/error-handling/error.tsx @@ -1,7 +1,7 @@ 'use client'; import { Boundary } from '#/ui/boundary'; -import Button from 'remote_4001/Button'; +// import Button from 'remote_4001/Button'; import React from 'react'; export default function Error({ error, reset }: any) { @@ -15,7 +15,13 @@ export default function Error({ error, reset }: any) {

Error

{error?.message}

- + + {/* */}
diff --git a/apps/next-app-router/next-app-router-4000/app/hooks/page.tsx b/apps/next-app-router/next-app-router-4000/app/hooks/page.tsx index c216fb991d6..62370239c48 100644 --- a/apps/next-app-router/next-app-router-4000/app/hooks/page.tsx +++ b/apps/next-app-router/next-app-router-4000/app/hooks/page.tsx @@ -1,32 +1,67 @@ +'use client'; +import Link from 'next/link'; +import Image from 'next/image'; +import Head from 'next/head'; +import Script from 'next/script'; +import { + useRouter, + usePathname, + useSearchParams, + useParams, + useSelectedLayoutSegments, + useSelectedLayoutSegment, +} from 'next/navigation'; import { ExternalLink } from '#/ui/external-link'; export default function Page() { - return ( -
-
-

Client Component Hooks

- -
    -
  • - Next.js provides a number of hooks for accessing routing information - from client components. -
  • -
  • - Try navigating each page and observing the output of each hook - called from the current routes layout.js and{' '} - page.js files. -
  • -
+ const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + const params = useParams(); + const segments = useSelectedLayoutSegments(); + const segment = useSelectedLayoutSegment(); -
- - Docs - - - Code - + return ( + <> + + Client Component Hooks Demo + +