mirror of
https://github.com/lobehub/lobe-chat.git
synced 2025-12-20 01:12:52 +08:00
* feat: integrate code inspector plugin and update turbopack configuration Signed-off-by: Innei <tukon479@gmail.com> * chore: add e2e env --------- Signed-off-by: Innei <tukon479@gmail.com>
355 lines
9.0 KiB
TypeScript
355 lines
9.0 KiB
TypeScript
import analyzer from '@next/bundle-analyzer';
|
|
import withSerwistInit from '@serwist/next';
|
|
import { codeInspectorPlugin } from 'code-inspector-plugin';
|
|
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';
|
|
const shouldUseCSP = process.env.ENABLED_CSP === '1';
|
|
|
|
const isTest =
|
|
process.env.NODE_ENV === 'test' || process.env.TEST === '1' || process.env.E2E === '1';
|
|
|
|
// if you need to proxy the api endpoint to remote server
|
|
|
|
const isStandaloneMode = buildWithDocker || isDesktop;
|
|
|
|
const standaloneConfig: NextConfig = {
|
|
output: 'standalone',
|
|
outputFileTracingIncludes: { '*': ['public/**/*', '.next/static/**/*'] },
|
|
};
|
|
|
|
const assetPrefix = process.env.NEXT_PUBLIC_ASSET_PREFIX;
|
|
|
|
const nextConfig: NextConfig = {
|
|
...(isStandaloneMode ? standaloneConfig : {}),
|
|
assetPrefix,
|
|
compiler: {
|
|
emotion: true,
|
|
},
|
|
compress: isProd,
|
|
experimental: {
|
|
optimizePackageImports: [
|
|
'emoji-mart',
|
|
'@emoji-mart/react',
|
|
'@emoji-mart/data',
|
|
'@icons-pack/react-simple-icons',
|
|
'@lobehub/ui',
|
|
'@lobehub/icons',
|
|
],
|
|
// 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'],
|
|
webpackBuildWorker: true,
|
|
webpackMemoryOptimizations: true,
|
|
},
|
|
async headers() {
|
|
const securityHeaders = [
|
|
{
|
|
key: 'x-robots-tag',
|
|
value: 'all',
|
|
},
|
|
];
|
|
|
|
if (shouldUseCSP) {
|
|
securityHeaders.push(
|
|
{
|
|
key: 'X-Frame-Options',
|
|
value: 'DENY',
|
|
},
|
|
{
|
|
key: 'Content-Security-Policy',
|
|
value: "frame-ancestors 'none';",
|
|
},
|
|
);
|
|
}
|
|
|
|
return [
|
|
{
|
|
headers: securityHeaders,
|
|
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: false,
|
|
// source: '/',
|
|
// },
|
|
{
|
|
destination: '/chat',
|
|
permanent: true,
|
|
source: '/welcome',
|
|
},
|
|
// 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', 'pdfkit'] : ['pdfkit'],
|
|
|
|
transpilePackages: ['pdfjs-dist', 'mermaid', 'better-auth-harmony'],
|
|
turbopack: {
|
|
rules: isTest
|
|
? void 0
|
|
: codeInspectorPlugin({
|
|
bundler: 'turbopack',
|
|
hotKeys: ['altKey'],
|
|
}),
|
|
},
|
|
|
|
typescript: {
|
|
ignoreBuildErrors: true,
|
|
},
|
|
|
|
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,
|
|
};
|
|
|
|
if (assetPrefix && (assetPrefix.startsWith('http://') || assetPrefix.startsWith('https://'))) {
|
|
// fix the Worker URL cross-origin issue
|
|
// refs: https://github.com/lobehub/lobe-chat/pull/9624
|
|
config.module.rules.push({
|
|
generator: {
|
|
// @see https://webpack.js.org/configuration/module/#rulegeneratorpublicpath
|
|
publicPath: '/_next/',
|
|
},
|
|
test: /worker\.ts$/,
|
|
// @see https://webpack.js.org/guides/asset-modules/
|
|
type: 'asset/resource',
|
|
});
|
|
}
|
|
|
|
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;
|
|
|
|
export default withBundleAnalyzer(withPWA(nextConfig as NextConfig));
|