const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); const withExportImages = require('next-export-optimize-images'); // You might need to insert additional domains in script-src if you are using external services const ContentSecurityPolicy = ` default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline' giscus.app comment.ivanli.cc localhost:8080; style-src 'self' 'unsafe-inline' comment.ivanli.cc localhost:8080; img-src * blob: data:; media-src 'none'; connect-src *; font-src 'self' comment.ivanli.cc localhost:8080; frame-src giscus.app `; const securityHeaders = [ // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP { key: 'Content-Security-Policy', value: ContentSecurityPolicy.replace(/\n/g, ''), }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin', }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options { key: 'X-Frame-Options', value: 'DENY', }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options { key: 'X-Content-Type-Options', value: 'nosniff', }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control { key: 'X-DNS-Prefetch-Control', value: 'on', }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains', }, // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()', }, ]; /** * @type {import('next/dist/next-server/server/config').NextConfig} **/ module.exports = withExportImages( withBundleAnalyzer({ reactStrictMode: true, pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'], eslint: { dirs: ['pages', 'components', 'lib', 'layouts', 'scripts'], }, images: { domains: ['pan.ivanli.cc', 'i.creativecommons.org'], }, async headers() { return [ { source: '/(.*)', headers: securityHeaders, }, ]; }, webpack: (config, { dev, isServer }) => { config.module.rules.push({ test: /\.(png|jpe?g|gif|mp4)$/i, use: [ { loader: 'file-loader', options: { publicPath: '/_next', name: 'static/media/[name].[hash].[ext]', }, }, ], }); config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); if (!dev && !isServer) { // Replace React with Preact only in client production build Object.assign(config.resolve.alias, { 'react/jsx-runtime.js': 'preact/compat/jsx-runtime', react: 'preact/compat', 'react-dom/test-utils': 'preact/test-utils', 'react-dom': 'preact/compat', }); } return config; }, trailingSlash: true, }) );