next.config.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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-Frame-Options",
  42. value: "SAMEORIGIN",
  43. },
  44. {
  45. key: "X-Content-Type-Options",
  46. value: "nosniff",
  47. },
  48. {
  49. key: "Permissions-Policy",
  50. value:
  51. "camera=(), microphone=(), geolocation=(), browsing-topics=()",
  52. },
  53. {
  54. key: "Referrer-Policy",
  55. value: "origin-when-cross-origin",
  56. },
  57. {
  58. key: "Content-Security-Policy",
  59. 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`,
  60. },
  61. ],
  62. })
  63. },
  64. async redirects() {
  65. return [
  66. {
  67. source: "/communities",
  68. destination: "/servers",
  69. permanent: true,
  70. },
  71. {
  72. source: "/imprint",
  73. destination: "/about#impressum",
  74. permanent: true,
  75. },
  76. {
  77. source: "/impressum",
  78. destination: "/about#impressum",
  79. permanent: true,
  80. },
  81. ]
  82. },
  83. webpack(config, { isServer, isdev }) {
  84. // Grab the existing rule that handles SVG imports
  85. const fileLoaderRule = config.module.rules.find(
  86. (rule) => rule.test && rule.test.test?.(".svg")
  87. )
  88. config.module.rules.push({
  89. oneOf: [
  90. // warning: do not specify `issuer` key here, it is broken with dynamic require
  91. // see https://github.com/webpack/webpack/issues/9309
  92. // https://github.com/vercel/next.js/discussions/15437
  93. {
  94. test: /\.svg$/i,
  95. resourceQuery: /inline/, // Only for *.svg?inline
  96. use: [{ loader: "@svgr/webpack", options: { svgo: false } }],
  97. },
  98. // we need to add this, as the previous rule disabled the default SVG loader
  99. {
  100. ...fileLoaderRule,
  101. test: /\.svg$/i,
  102. resourceQuery: { not: [/inline/] },
  103. },
  104. ],
  105. })
  106. // Modify the file loader rule to ignore *.svg, since we have it handled now.
  107. fileLoaderRule.exclude = /\.svg$/i
  108. return config
  109. },
  110. poweredByHeader: false,
  111. output: "standalone",
  112. }
  113. module.exports = nextConfig