Files
lobe-chat/next.config.ts
T
Arvin Xu 88e7d2ada1 💄 style: fix provider setting page hydration error (#8695)
* fix layout

goback

尝试开启生产环境react debug 模式

try to fix scroll issue

try to log settings layout pathname

try skip path

Revert "尝试移除 modal"

This reverts commit 7ffac36dcb680d5a9fdda6c675ad0adb66ed13d2.

remove debug

try to fix again

use InnerLink

fix

add InnerLink.tsx

add debug

* clean code

* fix modal cancel issue
2025-08-07 00:34:36 +08:00

348 lines
9.5 KiB
TypeScript

import analyzer from '@next/bundle-analyzer';
import { withSentryConfig } from '@sentry/nextjs';
import withSerwistInit from '@serwist/next';
import type { NextConfig } from 'next';
import ReactComponentName from 'react-scan/react-component-name/webpack';
const isProd = process.env.NODE_ENV === 'production';
const buildWithDocker = process.env.DOCKER === 'true';
const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
const enableReactScan = !!process.env.REACT_SCAN_MONITOR_API_KEY;
const isUsePglite = process.env.NEXT_PUBLIC_CLIENT_DB === 'pglite';
// if you need to proxy the api endpoint to remote server
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
const isStandaloneMode = buildWithDocker || isDesktop;
const standaloneConfig: NextConfig = {
output: 'standalone',
outputFileTracingIncludes: { '*': ['public/**/*', '.next/static/**/*'] },
};
const nextConfig: NextConfig = {
...(isStandaloneMode ? standaloneConfig : {}),
basePath,
compress: isProd,
experimental: {
optimizePackageImports: [
'emoji-mart',
'@emoji-mart/react',
'@emoji-mart/data',
'@icons-pack/react-simple-icons',
'@lobehub/ui',
'gpt-tokenizer',
],
// oidc provider depend on constructor.name
// but swc minification will remove the name
// so we need to disable it
// refs: https://github.com/lobehub/lobe-chat/pull/7430
serverMinification: false,
webVitalsAttribution: ['CLS', 'LCP'],
},
async headers() {
return [
{
headers: [
{
key: 'x-robots-tag',
value: 'all',
},
],
source: '/:path*',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/icons/(.*).(png|jpe?g|gif|svg|ico|webp)',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'Vercel-CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/images/(.*).(png|jpe?g|gif|svg|ico|webp)',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'Vercel-CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/videos/(.*).(mp4|webm|ogg|avi|mov|wmv|flv|mkv)',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'Vercel-CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/screenshots/(.*).(png|jpe?g|gif|svg|ico|webp)',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'Vercel-CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/og/(.*).(png|jpe?g|gif|svg|ico|webp)',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/favicon.ico',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/favicon-32x32.ico',
},
{
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'CDN-Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
source: '/apple-touch-icon.png',
},
];
},
logging: {
fetches: {
fullUrl: true,
hmrRefreshes: true,
},
},
reactStrictMode: true,
redirects: async () => [
{
destination: '/sitemap-index.xml',
permanent: true,
source: '/sitemap.xml',
},
{
destination: '/sitemap-index.xml',
permanent: true,
source: '/sitemap-0.xml',
},
{
destination: '/sitemap/plugins-1.xml',
permanent: true,
source: '/sitemap/plugins.xml',
},
{
destination: '/sitemap/assistants-1.xml',
permanent: true,
source: '/sitemap/assistants.xml',
},
{
destination: '/manifest.webmanifest',
permanent: true,
source: '/manifest.json',
},
{
destination: '/discover/assistant',
permanent: true,
source: '/discover/assistants',
},
{
destination: '/discover/plugin',
permanent: true,
source: '/discover/plugins',
},
{
destination: '/discover/model',
permanent: true,
source: '/discover/models',
},
{
destination: '/discover/provider',
permanent: true,
source: '/discover/providers',
},
{
destination: '/settings/common',
permanent: true,
source: '/settings',
},
{
destination: '/chat',
permanent: true,
source: '/welcome',
},
// TODO: 等 V2 做强制跳转吧
// {
// destination: '/settings/provider/volcengine',
// permanent: true,
// source: '/settings/provider/doubao',
// },
// we need back /repos url in the further
{
destination: '/files',
permanent: false,
source: '/repos',
},
],
// when external packages in dev mode with turbopack, this config will lead to bundle error
serverExternalPackages: isProd ? ['@electric-sql/pglite'] : undefined,
transpilePackages: ['pdfjs-dist', 'mermaid'],
webpack(config) {
config.experiments = {
asyncWebAssembly: true,
layers: true,
};
// 开启该插件会导致 pglite 的 fs bundler 被改表
if (enableReactScan && !isUsePglite) {
config.plugins.push(ReactComponentName({}));
}
// to fix shikiji compile error
// refs: https://github.com/antfu/shikiji/issues/23
config.module.rules.push({
resolve: {
fullySpecified: false,
},
test: /\.m?js$/,
type: 'javascript/auto',
});
// https://github.com/pinojs/pino/issues/688#issuecomment-637763276
config.externals.push('pino-pretty');
config.resolve.alias.canvas = false;
// to ignore epub2 compile error
// refs: https://github.com/lobehub/lobe-chat/discussions/6769
config.resolve.fallback = {
...config.resolve.fallback,
zipfile: false,
};
return config;
},
};
const noWrapper = (config: NextConfig) => config;
const withBundleAnalyzer = process.env.ANALYZE === 'true' ? analyzer() : noWrapper;
const withPWA =
isProd && !isDesktop
? withSerwistInit({
register: false,
swDest: 'public/sw.js',
swSrc: 'src/app/sw.ts',
})
: noWrapper;
const hasSentry = !!process.env.NEXT_PUBLIC_SENTRY_DSN;
const withSentry =
isProd && hasSentry
? (c: NextConfig) =>
withSentryConfig(
c,
{
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
// Suppresses source map uploading logs during build
silent: true,
},
{
// Enables automatic instrumentation of Vercel Cron Monitors.
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
// Hides source maps from generated client bundles
hideSourceMaps: true,
// Transpiles SDK to be compatible with IE11 (increases bundle size)
transpileClientSDK: true,
// Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. (increases server load)
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: '/monitoring',
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
},
)
: noWrapper;
export default withBundleAnalyzer(withPWA(withSentry(nextConfig) as NextConfig));