next.config.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. const { locales, defaultLocale } = require("./data/locales.js")
  2. function notIfProduction(param) {
  3. if (process.env.NODE_ENV === "production") return ""
  4. else return param
  5. }
  6. /** @type {import('next').NextConfig} */
  7. const nextConfig = {
  8. reactStrictMode: true,
  9. i18n: {
  10. locales: locales.map((l) => l.code),
  11. defaultLocale,
  12. },
  13. images: {
  14. remotePatterns: [
  15. { hostname: "proxy.joinmastodon.org" },
  16. { hostname: "c8.patreon.com" },
  17. { hostname: "c10.patreonusercontent.com" },
  18. ],
  19. },
  20. async headers() {
  21. // These static files are references with hardcoded URLs and need proper Cache-Control headers
  22. return [
  23. "/fonts/:all*(ttf|otf|woff|woff2)",
  24. "/favicon-:all*(png)",
  25. "/app-icon.png",
  26. "/preview.png",
  27. ]
  28. .map((source) => ({
  29. source,
  30. headers: [
  31. {
  32. key: "Cache-control",
  33. value: "max-age=3600, stale-while-revalidate",
  34. },
  35. ],
  36. }))
  37. .concat({
  38. source: "/(.*)?",
  39. headers: [
  40. {
  41. key: "X-Content-Type-Options",
  42. value: "nosniff",
  43. },
  44. {
  45. key: "Permissions-Policy",
  46. value:
  47. "camera=(), microphone=(), geolocation=(), browsing-topics=()",
  48. },
  49. {
  50. key: "Referrer-Policy",
  51. value: "origin-when-cross-origin",
  52. },
  53. {
  54. key: "Content-Security-Policy",
  55. value: `default-src 'self'; child-src 'none'; object-src 'none'; img-src 'self' proxy.joinmastodon.org blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' ${notIfProduction("'unsafe-inline' 'unsafe-eval'")}; connect-src 'self' api.joinmastodon.org; block-all-mixed-content`,
  56. },
  57. ],
  58. })
  59. },
  60. async redirects() {
  61. return [
  62. {
  63. source: "/communities",
  64. destination: "/servers",
  65. permanent: true,
  66. },
  67. {
  68. source: "/imprint",
  69. destination: "/about#impressum",
  70. permanent: true,
  71. },
  72. {
  73. source: "/impressum",
  74. destination: "/about#impressum",
  75. permanent: true,
  76. },
  77. ]
  78. },
  79. webpack(config, { isServer, isdev }) {
  80. // Grab the existing rule that handles SVG imports
  81. const fileLoaderRule = config.module.rules.find(
  82. (rule) => rule.test && rule.test.test?.(".svg")
  83. )
  84. config.module.rules.push({
  85. oneOf: [
  86. // warning: do not specify `issuer` key here, it is broken with dynamic require
  87. // see https://github.com/webpack/webpack/issues/9309
  88. // https://github.com/vercel/next.js/discussions/15437
  89. {
  90. test: /\.svg$/i,
  91. resourceQuery: /inline/, // Only for *.svg?inline
  92. use: [{ loader: "@svgr/webpack", options: { svgo: false } }],
  93. },
  94. // we need to add this, as the previous rule disabled the default SVG loader
  95. {
  96. ...fileLoaderRule,
  97. test: /\.svg$/i,
  98. resourceQuery: { not: [/inline/] },
  99. },
  100. ],
  101. })
  102. // Modify the file loader rule to ignore *.svg, since we have it handled now.
  103. fileLoaderRule.exclude = /\.svg$/i
  104. return config
  105. },
  106. poweredByHeader: false,
  107. output: "standalone",
  108. }
  109. module.exports = nextConfig