Repository navigation
Expand file tree
/
Copy pathnext.config.js
More file actions
181 lines (166 loc) · 7.74 KB
/
Copy pathnext.config.js
File metadata and controls
181 lines (166 loc) · 7.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
const path = require('node:path');
const withNextIntl = require('next-intl/plugin')('./core/lib/i18n/request.ts');
const { withSentryConfig } = require('@sentry/nextjs');
const { parseImageHosts, serializeImageRemotePatterns } = require('./image-hosts');
const { version: webPackageVersion } = require('./package.json');
const webBuildSha =
process.env.NEXT_PUBLIC_BUILD_SHA || process.env.VERCEL_GIT_COMMIT_SHA || process.env.GITHUB_SHA || 'dev';
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
});
const isProduction = process.env.NODE_ENV === 'production';
const isSentryEnabled = isProduction && process.env.SENTRY_DSN;
// Parse images hosts from environment variable (defaults when unset: image-hosts.js DEFAULT_IMAGE_HOSTS).
// The default list and the parsing live in image-hosts.js, shared with proxy.ts: this list is frozen
// into the build (images.remotePatterns below), while hosts added at runtime are served unoptimized
// by proxy.ts (core/lib/helpers/image-request.ts).
const parseImagesHosts = () => parseImageHosts(process.env.NEXT_PUBLIC_IMAGES_HOSTS);
const allowedImageHosts = parseImagesHosts();
const localImageHosts = [
{
hostname: '127.0.0.1',
port: '3000'
},
{
hostname: 'localhost',
port: '3030'
},
{
hostname: '127.0.0.1',
port: '3030'
}
];
// next/image optimizer allowlist (images.remotePatterns). BUILD-time: output 'standalone' serializes
// the resolved config into server.js, so container env cannot change it. The same list reaches
// proxy.ts through the EVER_TEAMS_OPTIMIZED_IMAGE_HOSTS env key below.
const imageRemotePatterns = [
// Static localhost patterns
...localImageHosts.map((host) => ({
protocol: 'http',
hostname: host.hostname,
port: host.port
})),
// Dynamic hosts from environment variable
...allowedImageHosts.map((hostname) => ({
protocol: 'https',
hostname: hostname,
port: ''
}))
];
const BUILD_OUTPUT_MODE = process.env.NEXT_BUILD_OUTPUT_TYPE;
// Left unset, Next takes the directory of the highest lockfile above apps/web as the workspace root,
// so a stray lockfile in a parent folder (e.g. the home directory) wins over the monorepo. The
// standalone output must stay laid out as apps/web/server.js (Docker CMD, Procfile, server-web).
const workspaceRoot = path.resolve(__dirname, '../..');
// Next.js 16: eslint configuration in next.config.js is no longer supported
// Use eslint.config.mjs for ESLint configuration instead
/** @type {import('next').NextConfig} */
const nextConfig = {
output: ['standalone', 'export'].includes(BUILD_OUTPUT_MODE) ? BUILD_OUTPUT_MODE : undefined,
outputFileTracingRoot: workspaceRoot,
// Next.js 16: Cache Components for explicit caching control
// Note: Disabled for now due to incompatibility with route segment config "runtime"
// cacheComponents: true,
experimental: {
// No optimizePackageImports list: Next 16 already rewrites lodash imports per method and optimizes
// react-icons/* and the @heroicons/react sets used here by default, while @radix-ui/react-icons and
// country-flag-icons/react/1x1 keep every icon in a single module that it cannot split.
// Turbopack caching is enabled by default in Next.js 16
},
// One turbopack object: a second `turbopack:` key would silently replace the first.
turbopack: {
root: workspaceRoot,
resolveAlias: {
// Turbopack ignores excludeDefaultMomentLocales, so the dynamic require('./locale/' + name) in
// moment.js bundles all 137 locale files on every route. The min build has no locale folder next
// to it, and the app only uses moment's built-in English locale. moment-timezone requires
// 'moment' too, so it resolves to the same instance.
// moment.locale('xx') and import 'moment/locale/xx' do not reach this instance: before using a
// moment locale, remove this alias (or point it at moment/min/moment-with-locales.min.js).
moment: 'moment/min/moment.min.js'
}
},
transpilePackages: [
// Their entry point is raw TypeScript (main: ./src/index.ts), so Next must compile them once imported.
'@ever-teams/constants',
'@ever-teams/types',
'@radix-ui/react-icons',
'@heroicons/react'
],
images: {
// Next.js 16: remotePatterns replaces deprecated domains config
remotePatterns: imageRemotePatterns
},
async rewrites() {
// Next.js 16: rewrites should return an object with beforeFiles, afterFiles, and fallback
return {
beforeFiles: [],
afterFiles: [
{
source: '/fonts/:path*',
destination: '/assets/fonts/:path*'
}
],
fallback: []
};
},
// The account deletion pages (public; the e-mailed link carries a deletion token in its fragment): no Referer for
// anything they load or link to, and not indexed. The pages also render <meta name="referrer">
// (app/[locale]/delete-account/layout.tsx). Static exports cannot send headers, so the block is left out there.
...(BUILD_OUTPUT_MODE === 'export'
? {}
: {
async headers() {
const accountDeletionHeaders = [
{ key: 'Referrer-Policy', value: 'no-referrer' },
{ key: 'X-Robots-Tag', value: 'noindex, nofollow' }
];
return [
{ source: '/delete-account/:path*', headers: accountDeletionHeaders },
{ source: '/:locale/delete-account/:path*', headers: accountDeletionHeaders }
];
}
}),
// Only BUILD-identity values belong here: Next inlines every key of this block into the client AND
// server bundles at build time. Branding (APP_NAME, APP_LOGO_URL, COMPANY_*, TERMS_LINK,
// MAIN_PICTURE*, ...) is deliberately NOT listed: it is read at runtime (env-config.ts
// readRuntimeEnv + the runtime env <script> in app/[locale]/layout.tsx), so a published Docker
// image can be rebranded with container env instead of a rebuild.
env: {
NEXT_PUBLIC_BUILD_VERSION: process.env.NEXT_PUBLIC_BUILD_VERSION || webPackageVersion,
NEXT_PUBLIC_BUILD_SHA: webBuildSha,
// NEXT_PUBLIC_SITE_* / NEXT_PUBLIC_WEB_APP_URL / NEXT_PUBLIC_TWITTER_USERNAME used to be listed here too;
// like every NEXT_PUBLIC_* they are read at runtime (readRuntimeEnv), never pinned by this block.
// Build-time by nature: the hosts the /_next/image optimizer accepts (images.remotePatterns is
// frozen into the build). proxy.ts compares them with the RUNTIME image hosts, and serves hosts
// allowed only at runtime unoptimized instead of letting the optimizer reject them.
EVER_TEAMS_OPTIMIZED_IMAGE_HOSTS: serializeImageRemotePatterns(imageRemotePatterns),
ANALYZE: process.env.ANALYZE
// NEXT_PUBLIC_DEMO is automatically accessible (no need to add it here)
}
};
// Injected content via Sentry wizard below
const sentryWebpackPluginOptions = {
org: process.env.SENTRY_ORG || 'ever-co',
project: process.env.SENTRY_PROJECT || 'ever-teams-web',
// An auth token is required for uploading source maps.
authToken: process.env.SENTRY_AUTH_TOKEN,
silent: true, // Suppresses all logs
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
// Webpack builds only: Turbopack, the `next build` default, ignores this option.
webpack: {
treeshake: {
removeDebugLogging: true
}
}
// No tunnelRoute: proxy.ts runs before rewrites and its matcher catches the tunnel path, so next-intl would
// rewrite or redirect Sentry's requests before the SDK's rewrite applies, and a static export
// (NEXT_BUILD_OUTPUT_TYPE=export) cannot serve a tunnel at all. Exclude the path from that matcher first.
// Additional config options for the Sentry Webpack plugin.
// Keep in mind that https://github.com/getsentry/sentry-webpack-plugin#options.
};
// Make sure adding Sentry options is the last code to run before exporting
module.exports = isSentryEnabled
? withSentryConfig(withNextIntl(withBundleAnalyzer(nextConfig)), sentryWebpackPluginOptions)
: withNextIntl(withBundleAnalyzer(nextConfig));