MSW v2.7.0 fallback layer covering dashboard/users/exams/grades domains. NEXT_PUBLIC_MSW=1 enables browser Service Worker + SSR route handler mock responses without backend. Production build excludes all mock data via Turbopack resolveAlias redirecting @/mocks to empty stub. Acceptance: build bundle (client+server) verified clean of mock strings; typecheck/lint/vitest (231 tests) all pass.
109 lines
3.9 KiB
JavaScript
109 lines
3.9 KiB
JavaScript
/**
|
||
* 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);
|