-
Notifications
You must be signed in to change notification settings - Fork 5
/
next.config.js
364 lines (333 loc) · 17.4 KB
/
next.config.js
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
const bundleAnalyzer = require('@next/bundle-analyzer');
const nextSourceMaps = require('@zeit/next-source-maps');
const packageJson = require('./package');
const withSourceMaps = nextSourceMaps();
const withBundleAnalyzer = bundleAnalyzer({ // Run with "yarn analyse:bundle" - See https://www.npmjs.com/package/@next/bundle-analyzer
enabled: process.env.ANALYZE_BUNDLE === 'true',
});
const date = new Date();
const GIT_COMMIT_SHA_SHORT = typeof process.env.GIT_COMMIT_SHA === 'string' && process.env.GIT_COMMIT_SHA.substring(0, 8);
console.debug(`Building Next with NODE_ENV="${process.env.NODE_ENV}" NEXT_PUBLIC_APP_STAGE="${process.env.NEXT_PUBLIC_APP_STAGE}" using GIT_COMMIT_SHA=${process.env.GIT_COMMIT_SHA} and GIT_COMMIT_REF=${process.env.GIT_COMMIT_REF}`);
const GIT_COMMIT_TAGS = (process.env.GIT_COMMIT_TAGS ? process.env.GIT_COMMIT_TAGS.trim() : '').replace('refs/tags/', '');
console.debug(`Deployment will be tagged automatically, using GIT_COMMIT_TAGS: "${GIT_COMMIT_TAGS}"`);
// Iterate over all tags and extract the first the match "v*"
const APP_RELEASE_TAG = GIT_COMMIT_TAGS ? GIT_COMMIT_TAGS.split(' ').find((tag) => tag.startsWith('v')) : `unknown-${GIT_COMMIT_SHA_SHORT}`;
console.debug(`Release version resolved from tags: "${APP_RELEASE_TAG}" (matching first tag starting with "v")`);
/**
* This file is for advanced configuration of the Next.js framework.
*
* The below config applies to the whole application.
* next.config.js gets used by the Next.js server and build phases, and it's not included in the browser build.
*
* XXX Not all configuration options are listed below, we only kept those of most interest.
* You'll need to dive into Next.js own documentation to find out about what's not included.
* Basically, we focused on options that seemed important for a SSG/SSR app running on serverless mode (Vercel).
* Also, we included some options by are not using them, this is mostly to help make you aware of those options, in case you'd need them.
*
* @see https://nextjs.org/docs/api-reference/next.config.js/introduction
*/
module.exports = withBundleAnalyzer(withSourceMaps({
// basepath: '', // If you want Next.js to cover only a subsection of the domain. See https://nextjs.org/docs/api-reference/next.config.js/basepath
// target: 'serverless', // Automatically enabled on Vercel, you may need to manually opt-in if you're not using Vercel. See https://nextjs.org/docs/api-reference/next.config.js/build-target#serverless-target
// trailingSlash: false, // By default Next.js will redirect urls with trailing slashes to their counterpart without a trailing slash. See https://nextjs.org/docs/api-reference/next.config.js/trailing-slash
/**
* React's Strict Mode is a development mode only feature for highlighting potential problems in an application.
* It helps to identify unsafe lifecycles, legacy API usage, and a number of other features.
*
* Officially suggested by Next.js:
* We strongly suggest you enable Strict Mode in your Next.js application to better prepare your application for the future of React.
*
* If you or your team are not ready to use Strict Mode in your entire application, that's OK! You can incrementally migrate on a page-by-page basis using <React.StrictMode>.
*
* @see https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode
*/
reactStrictMode: true,
// TODO doc
images: {
domains: [],
},
/**
* Environment variables added to JS bundle
*
* XXX All env variables defined in ".env*" files that aren't public (those that don't start with "NEXT_PUBLIC_") MUST manually be made available at build time below.
* They're necessary on Vercel for runtime execution (SSR, SSG with revalidate, everything that happens server-side will need those).
*
* XXX This is a duplication of the environment variables.
* The variables defined below are only used locally, while those in "vercel.*.json:build:env" will be used on the Vercel platform.
* See https://vercel.com/docs/v2/build-step/#providing-environment-variables
*
* @see https://nextjs.org/docs/api-reference/next.config.js/environment-variables
*/
env: {
GITHUB_DISPATCH_TOKEN: process.env.GITHUB_DISPATCH_TOKEN,
SENTRY_DSN: process.env.SENTRY_DSN,
// Vercel env variables - See https://vercel.com/docs/environment-variables#system-environment-variables
VERCEL: process.env.VERCEL,
VERCEL_ENV: process.env.VERCEL_ENV,
VERCEL_URL: process.env.VERCEL_URL,
CI: process.env.CI,
// Dynamic env variables
NEXT_PUBLIC_APP_DOMAIN: process.env.VERCEL_URL, // Alias
NEXT_PUBLIC_APP_BASE_URL: process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : 'http://localhost:9999',
NEXT_PUBLIC_APP_BUILD_TIME: date.toString(),
NEXT_PUBLIC_APP_BUILD_TIMESTAMP: +date,
NEXT_PUBLIC_APP_NAME: packageJson.name,
NEXT_PUBLIC_APP_NAME_VERSION: `${packageJson.name}-${APP_RELEASE_TAG}`,
GIT_COMMIT_SHA_SHORT,
GIT_COMMIT_SHA: process.env.GIT_COMMIT_SHA, // Resolve commit hash from ENV first (set through CI), fallbacks to reading git (when used locally, through "/scripts/populate-git-env.sh")
GIT_COMMIT_REF: process.env.GIT_COMMIT_REF, // Resolve commit ref (branch/tag) from ENV first (set through CI), fallbacks to reading git (when used locally, through "/scripts/populate-git-env.sh")
GIT_COMMIT_TAGS: process.env.GIT_COMMIT_TAGS || '', // Resolve commit tags/releases from ENV first (set through CI), fallbacks to reading git (when used locally, through "/scripts/populate-git-env.sh")
},
/**
* Headers allow you to set custom HTTP headers for an incoming request path.
*
* Headers allow you to set route specific headers like CORS headers, content-types, and any other headers that may be needed.
* They are applied at the very top of the routes.
*
* @example source: '/(.*?)', // Match all paths, including "/"
* @example source: '/:path*', // Match all paths, excluding "/"
*
* @return {Promise<Array<{ headers: [{value: string, key: string}], source: string }>>}
* @see https://nextjs.org/docs/api-reference/next.config.js/headers
* @since 9.5 - See https://nextjs.org/blog/next-9-5#headers
*/
async headers() {
// Disabling embedding by default is safer.
const DISABLE_IFRAME_EMBED_FROM_3RD_PARTIES = true;
const headers = [
{
// Make all fonts immutable and cached for one year
'source': '/static/fonts/(.*?)',
'headers': [
{
'key': 'Cache-Control',
// See https://www.keycdn.com/blog/cache-control-immutable#what-is-cache-control-immutable
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#browser_compatibility
'value': `public, max-age=31536000, immutable`,
},
],
},
{
// Make all other static assets immutable and cached for one hour
'source': '/static/(.*?)',
'headers': [
{
'key': 'Cache-Control',
// See https://www.keycdn.com/blog/cache-control-immutable#what-is-cache-control-immutable
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#browser_compatibility
'value': `public, max-age=3600, immutable`,
},
],
},
{
source: '/(.*?)', // Match all paths, including "/" - See https://github.com/vercel/next.js/discussions/17991#discussioncomment-112028
headers: [
// This directive helps protect against some XSS attacks
// See https://infosec.mozilla.org/guidelines/web_security#x-content-type-options
{
key: 'X-Content-Type-Options',
value: `nosniff`,
},
],
},
{
source: '/(.*?)', // Match all paths, including "/" - See https://github.com/vercel/next.js/discussions/17991#discussioncomment-112028
headers: [
// This directive helps protect user's privacy and might avoid leaking sensitive data in urls to 3rd parties (e.g: when loading a 3rd party asset)
// See https://infosec.mozilla.org/guidelines/web_security#referrer-policy
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
// See https://scotthelme.co.uk/a-new-security-header-referrer-policy/
{
key: 'Referrer-Policy',
// "no-referrer-when-downgrade" is the default behaviour
// XXX You might want to restrict even more the referrer policy
value: `no-referrer-when-downgrade`,
},
],
},
{
source: '/(.*?)', // Match all paths, including "/" - See https://github.com/vercel/next.js/discussions/17991#discussioncomment-112028
// source: '/:path*', // Match all paths, excluding "/"
headers: [
{
key: 'X-Content-Type-Options',
value: `nosniff`,
},
],
},
];
// When 3rd party embeds are forbidden, only allow same origin to embed iframe by default
// This is a stronger default, if you don't to embed your site in any external website
if (DISABLE_IFRAME_EMBED_FROM_3RD_PARTIES) {
headers.push({
source: '/(.*?)', // Match all paths, including "/" - See https://github.com/vercel/next.js/discussions/17991#discussioncomment-112028
headers: [
{
// This directive's "ALLOW-FROM" option is deprecated in favor of "Content-Security-Policy" "frame-ancestors"
// So, we use a combination of both the CSP directive and the "X-Frame-Options" for browser that don't support CSP
// See https://infosec.mozilla.org/guidelines/web_security#x-frame-options
key: 'X-Frame-Options',
value: `SAMEORIGIN`,
},
],
});
headers.push({
source: '/(.*?)', // Match all paths, including "/" - See https://github.com/vercel/next.js/discussions/17991#discussioncomment-112028
// See https://infosec.mozilla.org/guidelines/web_security#x-frame-options
headers: [
{
key: 'Content-Security-Policy',
value: `frame-ancestors 'self`,
},
],
});
}
console.info('Using headers:', JSON.stringify(headers, null, 2));
return headers;
},
/**
* Rewrites allow you to map an incoming request path to a different destination path.
*
* Rewrites are only available on the Node.js environment and do not affect client-side routing.
* Rewrites are the most commonly used form of custom routing — they're used for dynamic routes (pretty URLs), user-land routing libraries (e.g. next-routes), internationalization, and other advanced use cases.
*
* For example, the route /user/:id rendering a specific user's profile page is a rewrite.
* Rendering your company's about page for both /about and /fr/a-propos is also a rewrite.
* The destination url can be internal, or external.
*
* @return { Promise<Array<{ destination: string, source: string, headers: Array<{ key: string, value: string }> }>> }
* @see https://nextjs.org/docs/api-reference/next.config.js/rewrites
* @since 9.5 - See https://nextjs.org/blog/next-9-5#rewrites
*/
async rewrites() {
const rewrites = [
// Robots rewrites
{
source: `/robots.txt`,
destination: process.env.NEXT_PUBLIC_APP_STAGE === 'production' ? `/robots/production.txt` : '/robots/!production.txt',
},
];
console.info('Using rewrites:', rewrites);
return rewrites;
},
/**
* Redirects allow you to redirect an incoming request path to a different destination path.
*
* Redirects are only available on the Node.js environment and do not affect client-side routing.
* By redirects, we mean HTTP Redirects (aka URL forwarding).
* Redirects are most commonly used when a website is reorganized — ensuring search engines and bookmarks are forwarded to their new locations.
* The destination url can be internal, or external.
*
* @return { Promise<Array<{ permanent: boolean, destination: string, source: string, statusCode?: number }>> }
* @see https://nextjs.org/docs/api-reference/next.config.js/redirects
* @since 9.5 - See https://nextjs.org/blog/next-9-5#redirects
*/
async redirects() {
const redirects = [];
console.info('Using redirects:', redirects);
return redirects;
},
// See https://nextjs.org/docs/messages/webpack5
// Necessary to manually specify to use webpack 5, because we use a custom "webpack" config (see below)
webpack5: true,
resolve: {
fallback: {
// Fixes npm packages that depend on `fs` module
fs: false,
},
},
/**
* The webpack function is executed twice, once for the server and once for the client.
* This allows you to distinguish between client and server configuration using the isServer property.
*
* @param config Current webpack config. Useful to reuse parts of what's already configured while overridding other parts.
* @param buildId The build id, used as a unique identifier between builds.
* @param dev Indicates if the compilation will be done in development.
* @param isServer It's true for server-side compilation, and false for client-side compilation.
* @param defaultLoaders Default loaders used internally by Next.js:
* - babel Default babel-loader configuration
* @see https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
*/
webpack: (config, {
buildId,
dev,
isServer,
defaultLoaders,
}) => {
if (isServer) {
/**
* This special server-only environment variable isn't string-replaced by webpack during bundling (it isn't added to the DefinePlugin definitions).
*
* Therefore, it's:
* - Always '1' on the server, during development
* - Always '1' on the server, during the Next.js build step
* - Always undefined on the browser
* - Always undefined in API endpoints
* - Always undefined during static pages re-generations (ISG) and server-side pages
*
* It can be useful when performing processing that should only happen during the initial build, or not during the initial build.
*/
process.env.IS_SERVER_INITIAL_BUILD = '1';
}
const APP_VERSION_RELEASE = APP_RELEASE_TAG || buildId;
config.plugins.map((plugin, i) => {
// Inject custom environment variables in "DefinePlugin" - See https://webpack.js.org/plugins/define-plugin/
if (plugin.__proto__.constructor.name === 'DefinePlugin') {
// Dynamically add some "public env" variables that will be replaced during the build through "DefinePlugin"
// Those variables are considered public because they are available at build time and at run time (they'll be replaced during initial build, by their value)
plugin.definitions['process.env.NEXT_PUBLIC_APP_BUILD_ID'] = JSON.stringify(buildId);
plugin.definitions['process.env.NEXT_PUBLIC_APP_VERSION_RELEASE'] = JSON.stringify(APP_VERSION_RELEASE);
}
});
if (isServer) { // Trick to only log once
console.debug(`[webpack] Building release "${APP_VERSION_RELEASE}" using NODE_ENV="${process.env.NODE_ENV}" ${process.env.IS_SERVER_INITIAL_BUILD ? 'with IS_SERVER_INITIAL_BUILD="1"' : ''}`);
}
// XXX See https://github.com/vercel/next.js/blob/canary/examples/with-sentry-simple/next.config.js
// In `pages/_app.js`, Sentry is imported from @sentry/node. While
// @sentry/browser will run in a Node.js environment, @sentry/node will use
// Node.js-only APIs to catch even more unhandled exceptions.
//
// This works well when Next.js is SSRing your page on a server with
// Node.js, but it is not what we want when your client-side bundle is being
// executed by a browser.
//
// Luckily, Next.js will call this webpack function twice, once for the
// server and once for the client. Read more:
// https://nextjs.org/docs#customizing-webpack-config
//
// So ask Webpack to replace @sentry/node imports with @sentry/browser when
// building the browser's bundle
if (!isServer) {
config.resolve.alias['@sentry/node'] = '@sentry/browser';
}
return config;
},
/**
* Next.js uses a constant id generated at build time to identify which version of your application is being served.
*
* This can cause problems in multi-server deployments when next build is ran on every server.
* In order to keep a static build id between builds you can provide your own build id.
*
* XXX We documented this function in case you might want to use it, but we aren't using it.
*
* @see https://nextjs.org/docs/api-reference/next.config.js/configuring-the-build-id
*/
// generateBuildId: async () => {
// // You can, for example, get the latest git commit hash here
// return 'my-build-id'
// },
/**
* Next.js exposes some options that give you some control over how the server will dispose or keep in memory built pages in development.
*
* XXX We documented this function in case you might want to use it, but we aren't using it.
*
* @see https://nextjs.org/docs/api-reference/next.config.js/configuring-onDemandEntries
*/
// onDemandEntries: {
// // period (in ms) where the server will keep pages in the buffer
// maxInactiveAge: 25 * 1000,
// // number of pages that should be kept simultaneously without being disposed
// pagesBufferLength: 2,
// },
poweredByHeader: false, // See https://nextjs.org/docs/api-reference/next.config.js/disabling-x-powered-by
}));