next.config.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /**
  2. * == Notes for production build==
  3. * The modules required from this file must be transpiled before running `next build`.
  4. *
  5. * See: https://github.com/vercel/next.js/discussions/35969#discussioncomment-2522954
  6. */
  7. import type { NextConfig } from 'next';
  8. import {
  9. PHASE_PRODUCTION_BUILD,
  10. PHASE_PRODUCTION_SERVER,
  11. } from 'next/constants';
  12. import path from 'node:path';
  13. import bundleAnalyzer from '@next/bundle-analyzer';
  14. import nextI18nConfig from './config/next-i18next.config';
  15. import { listPrefixedPackages } from './src/utils/next.config.utils';
  16. const { i18n } = nextI18nConfig;
  17. const getTranspilePackages = (): string[] => {
  18. const packages = [
  19. // listing ESM packages until experimental.esmExternals works correctly to avoid ERR_REQUIRE_ESM
  20. 'react-markdown',
  21. 'unified',
  22. 'markdown-table',
  23. 'bail',
  24. 'ccount',
  25. 'character-entities',
  26. 'character-entities-html4',
  27. 'character-entities-legacy',
  28. 'comma-separated-tokens',
  29. 'decode-named-character-reference',
  30. 'devlop',
  31. 'fault',
  32. 'escape-string-regexp',
  33. 'hastscript',
  34. 'html-void-elements',
  35. 'is-absolute-url',
  36. 'is-plain-obj',
  37. 'longest-streak',
  38. 'micromark',
  39. 'property-information',
  40. 'space-separated-tokens',
  41. 'stringify-entities',
  42. 'trim-lines',
  43. 'trough',
  44. 'web-namespaces',
  45. 'vfile',
  46. 'vfile-location',
  47. 'vfile-message',
  48. 'zwitch',
  49. 'emoticon',
  50. 'direction', // for hast-util-select
  51. 'bcp-47-match', // for hast-util-select
  52. 'parse-entities',
  53. 'character-reference-invalid',
  54. 'is-hexadecimal',
  55. 'is-alphabetical',
  56. 'is-alphanumerical',
  57. 'github-slugger',
  58. 'html-url-attributes',
  59. 'estree-util-is-identifier-name',
  60. 'superjson',
  61. ...listPrefixedPackages([
  62. 'remark-',
  63. 'rehype-',
  64. 'hast-',
  65. 'mdast-',
  66. 'micromark-',
  67. 'unist-',
  68. ]),
  69. ];
  70. return packages;
  71. };
  72. const optimizePackageImports: string[] = [
  73. '@growi/core',
  74. '@growi/editor',
  75. '@growi/pluginkit',
  76. '@growi/presentation',
  77. '@growi/preset-themes',
  78. '@growi/remark-attachment-refs',
  79. '@growi/remark-drawio',
  80. '@growi/remark-growi-directive',
  81. '@growi/remark-lsx',
  82. '@growi/slack',
  83. '@growi/ui',
  84. ];
  85. export default (phase: string): NextConfig => {
  86. /** @type {import('next').NextConfig} */
  87. const nextConfig: NextConfig = {
  88. reactStrictMode: true,
  89. poweredByHeader: false,
  90. pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
  91. i18n,
  92. serverExternalPackages: [
  93. 'handsontable', // Legacy v6.2.2 requires @babel/polyfill which is unavailable; client-only via dynamic import
  94. ],
  95. // for build
  96. typescript: {
  97. tsconfigPath: 'tsconfig.build.client.json',
  98. },
  99. transpilePackages:
  100. phase !== PHASE_PRODUCTION_SERVER ? getTranspilePackages() : undefined,
  101. sassOptions: {
  102. loadPaths: [path.resolve(__dirname, 'src')],
  103. },
  104. experimental: {
  105. optimizePackageImports,
  106. },
  107. turbopack: {
  108. rules: {
  109. // Server-only: auto-wrap getServerSideProps with SuperJSON serialization
  110. '*.page.ts': [
  111. {
  112. condition: { not: 'browser' },
  113. loaders: [
  114. path.resolve(__dirname, 'src/utils/superjson-ssr-loader.ts'),
  115. ],
  116. as: '*.ts',
  117. },
  118. ],
  119. '*.page.tsx': [
  120. {
  121. condition: { not: 'browser' },
  122. loaders: [
  123. path.resolve(__dirname, 'src/utils/superjson-ssr-loader.ts'),
  124. ],
  125. as: '*.tsx',
  126. },
  127. ],
  128. },
  129. resolveAlias: {
  130. // Exclude fs from client bundle
  131. fs: { browser: './src/lib/empty-module.ts' },
  132. // Exclude server-only packages from client bundle
  133. 'dtrace-provider': { browser: './src/lib/empty-module.ts' },
  134. mongoose: { browser: './src/lib/empty-module.ts' },
  135. 'i18next-fs-backend': { browser: './src/lib/empty-module.ts' },
  136. bunyan: { browser: './src/lib/empty-module.ts' },
  137. 'bunyan-format': { browser: './src/lib/empty-module.ts' },
  138. 'core-js': { browser: './src/lib/empty-module.ts' },
  139. },
  140. },
  141. };
  142. // production server — skip bundle analyzer
  143. if (phase === PHASE_PRODUCTION_SERVER) {
  144. return nextConfig;
  145. }
  146. const withBundleAnalyzer = bundleAnalyzer({
  147. enabled:
  148. phase === PHASE_PRODUCTION_BUILD &&
  149. (process.env.ANALYZE === 'true' || process.env.ANALYZE === '1'),
  150. });
  151. return withBundleAnalyzer(nextConfig);
  152. };