/** * Next.js 配置(v2.1 M8 + v0.2 Tailwind v4 + Next 16 Turbopack + P1-4 next-intl + P1-5 MSW) * * 角色:插件化仪表盘宿主(单 Next.js App Router · 单 Docker) * - output:standalone(单容器部署) * - transpilePackages: @edu/* workspace 包 * - 反向代理:/api/v1/* → api-gateway :8080(JWT 校验 + 注入 x-user-id/x-user-role) * - GraphQL 查询走 apollo-router :3000(M8 验收点,由 Apollo Client 直连) * - next-intl:无 i18n 路由模式(locale 由 cookie 决定,ARCHITECTURE.md §3.4 V3-A6) * - MSW 兜底层(P1-5):NEXT_PUBLIC_MSW!=1 时把 @/mocks 与 @/mocks/graphql-data * 重定向到空 stub,确保生产 bundle 不含 mock 数据(ARCHITECTURE.md §3.4 V3-A7) * * Next 16 默认 Turbopack: * - turbopack.resolveExtensions 处理 ESM 包 .js 后缀导入源码 TS 文件的映射 * - webpack 配置保留作为 fallback(--webpack flag 时生效) * * 关联:portal-shell ARCHITECTURE.md §3.4 V3-A6/V3-A7、spec §2、project_rules §3.2 */ import path from "node:path"; import { fileURLToPath } from "node:url"; import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); const __dirname = path.dirname(fileURLToPath(import.meta.url)); // P1-5:MSW 启用开关。生产构建(NEXT_PUBLIC_MSW 非等于 "1")时把 // @/mocks 与 @/mocks/graphql-data 重定向到空 stub, // 确保 bundle 不含真实 mock 数据与 msw 包。 // 注意:Turbopack resolveAlias 不支持 Windows 绝对路径("windows imports // are not implemented yet"),故使用 @/mocks/empty 说明符(由 tsconfig // paths 解析为 src/mocks/empty.ts),而非绝对路径。 const MSW_ENABLED = process.env.NEXT_PUBLIC_MSW === "1"; const MOCKS_STUB_SPECIFIER = "@/mocks/empty"; /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, output: "standalone", transpilePackages: [ "@edu/ui-components", "@edu/ui-tokens", "@edu/hooks", "@edu/contracts", "@edu/shared-ts", ], experimental: { serverActions: { bodySizeLimit: "2mb" }, }, // Turbopack 配置(Next 16 默认):处理 ESM 包 .js 后缀导入源码 .ts/.tsx 文件 turbopack: { resolveExtensions: [ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ], // P1-5:MSW 关闭时把 @/mocks(MswProvider 静态 import)与 // @/mocks/graphql-data(route.ts 静态 import)重定向到空 stub。 // 使用 @/mocks/empty 说明符作为目标(Turbopack 不支持 Windows 绝对路径)。 ...(MSW_ENABLED ? {} : { resolveAlias: { "@/mocks": MOCKS_STUB_SPECIFIER, "@/mocks/graphql-data": MOCKS_STUB_SPECIFIER, }, }), }, // Webpack 配置(fallback,使用 --webpack flag 时生效) webpack(config) { config.resolve = config.resolve || {}; config.resolve.extensionAlias = { ...config.resolve.extensionAlias, ".js": [".ts", ".tsx", ".js"], }; // P1-5:MSW 关闭时把 @/mocks 与 @/mocks/graphql-data 重定向到空 stub。 // webpack resolve.alias 支持 exact 匹配($ 后缀)与前缀匹配, // 这里用 exact 匹配避免误伤其他 @/mocks/xxx 路径(实际只有这两个 import 点)。 if (!MSW_ENABLED) { config.resolve.alias = { ...config.resolve.alias, "@/mocks$": MOCKS_STUB_SPECIFIER, "@/mocks/graphql-data$": MOCKS_STUB_SPECIFIER, }; } return config; }, async rewrites() { const gateway = process.env.API_GATEWAY_URL || "http://localhost:8080"; return [ { source: "/api/v1/:path*", destination: `${gateway}/api/v1/:path*`, }, { source: "/api/auth/:path*", destination: `${gateway}/api/v1/iam/:path*`, }, ]; }, }; export default withNextIntl(nextConfig);