Browse Source

enable turbopack

Yuki Takei 1 month ago
parent
commit
22b68fa023

+ 2 - 8
apps/app/config/next-i18next.config.js

@@ -1,5 +1,4 @@
 const isDev = process.env.NODE_ENV === 'development';
-
 // biome-ignore lint/style/useNodejsImportProtocol: ignore
 const path = require('path');
 
@@ -8,8 +7,6 @@ const { isServer } = require('@growi/core/dist/utils');
 
 const { defaultLang } = require('./i18next.config');
 
-const HMRPlugin = isDev ? require('i18next-hmr/plugin').HMRPlugin : undefined;
-
 /** @type {import('next-i18next').UserConfig} */
 module.exports = {
   ...require('./i18next.config').initOptions,
@@ -24,11 +21,8 @@ module.exports = {
 
   use: isDev
     ? isServer()
-      ? [new HMRPlugin({ webpack: { server: true } })]
-      : [
-          require('i18next-chained-backend').default,
-          new HMRPlugin({ webpack: { client: true } }),
-        ]
+      ? []
+      : [require('i18next-chained-backend').default]
     : [],
   backend: {
     backends: isServer()

+ 36 - 0
apps/app/next.config.ts

@@ -117,6 +117,42 @@ export default (phase: string): NextConfig => {
       optimizePackageImports,
     },
 
+    turbopack: {
+      rules: {
+        // Server-only: auto-wrap getServerSideProps with SuperJSON serialization
+        '*.page.ts': [
+          {
+            condition: { not: 'browser' },
+            loaders: [
+              path.resolve(__dirname, 'src/utils/superjson-ssr-loader.ts'),
+            ],
+            as: '*.ts',
+          },
+        ],
+        '*.page.tsx': [
+          {
+            condition: { not: 'browser' },
+            loaders: [
+              path.resolve(__dirname, 'src/utils/superjson-ssr-loader.ts'),
+            ],
+            as: '*.tsx',
+          },
+        ],
+      },
+      resolveAlias: {
+        // Exclude fs from client bundle
+        fs: { browser: './src/lib/empty-module.ts' },
+        // Exclude server-only packages from client bundle
+        'dtrace-provider': { browser: './src/lib/empty-module.ts' },
+        mongoose: { browser: './src/lib/empty-module.ts' },
+        'mathjax-full': { browser: './src/lib/empty-module.ts' },
+        'i18next-fs-backend': { browser: './src/lib/empty-module.ts' },
+        bunyan: { browser: './src/lib/empty-module.ts' },
+        'bunyan-format': { browser: './src/lib/empty-module.ts' },
+        'core-js': { browser: './src/lib/empty-module.ts' },
+      },
+    },
+
     webpack(config, options) {
       // Auto-wrap getServerSideProps with superjson serialization (replaces next-superjson SWC plugin)
       if (options.isServer) {

+ 13 - 0
apps/app/src/lib/empty-module.spec.ts

@@ -0,0 +1,13 @@
+import { describe, expect, it } from 'vitest';
+
+describe('empty-module', () => {
+  it('should have a default export', async () => {
+    const mod = await import('./empty-module');
+    expect(mod).toHaveProperty('default');
+  });
+
+  it('should export an empty object as default', async () => {
+    const mod = await import('./empty-module');
+    expect(mod.default).toEqual({});
+  });
+});

+ 3 - 0
apps/app/src/lib/empty-module.ts

@@ -0,0 +1,3 @@
+// Empty module used as a Turbopack resolveAlias target for server-only packages excluded from the client bundle
+// biome-ignore lint/style/noDefaultExport: Required as alias target for various import styles
+export default {};

+ 0 - 145
apps/app/src/test/nextjs-v16-upgrade.spec.ts

@@ -1,145 +0,0 @@
-import { readFileSync } from 'node:fs';
-import path from 'node:path';
-import { describe, expect, it } from 'vitest';
-
-const appDir = path.resolve(__dirname, '../..');
-
-describe('Next.js v16 Upgrade', () => {
-  const packageJson = JSON.parse(
-    readFileSync(path.join(appDir, 'package.json'), 'utf-8'),
-  );
-
-  describe('dependency versions', () => {
-    it('should have next ^16.0.0', () => {
-      expect(packageJson.dependencies.next).toBe('^16.0.0');
-    });
-
-    it('should have @next/bundle-analyzer ^16.0.0', () => {
-      expect(packageJson.devDependencies['@next/bundle-analyzer']).toBe(
-        '^16.0.0',
-      );
-    });
-
-    it('should keep react at ^18.2.0', () => {
-      expect(packageJson.dependencies.react).toBe('^18.2.0');
-    });
-
-    it('should keep react-dom at ^18.2.0', () => {
-      expect(packageJson.dependencies['react-dom']).toBe('^18.2.0');
-    });
-  });
-
-  describe('build scripts', () => {
-    it('should include --webpack flag in build:client', () => {
-      expect(packageJson.scripts['build:client']).toBe('next build --webpack');
-    });
-
-    it('should not change start script (no bundler at runtime)', () => {
-      expect(packageJson.scripts.start).toBe('next start');
-    });
-  });
-
-  describe('Sass tilde imports', () => {
-    it('should not use tilde prefix for node_modules imports in toastr.scss', () => {
-      const toastrScss = readFileSync(
-        path.join(appDir, 'src/styles/molecules/toastr.scss'),
-        'utf-8',
-      );
-      // Should not have ~react-toastify (node_modules tilde)
-      expect(toastrScss).not.toMatch(/@import\s+['"]~react-toastify/);
-      // Should have the import without tilde
-      expect(toastrScss).toMatch(
-        /@import\s+['"]react-toastify\/scss\/main['"]/,
-      );
-    });
-  });
-
-  describe('measurement script', () => {
-    it('should use --webpack flag in next dev command', () => {
-      const script = readFileSync(
-        path.join(appDir, 'bin/measure-chunk-stats.sh'),
-        'utf-8',
-      );
-      expect(script).toContain('next dev');
-      expect(script).toMatch(/next dev\b.*--webpack/);
-    });
-
-    it('should clean .next/dev directory for v16 isolated dev builds', () => {
-      const script = readFileSync(
-        path.join(appDir, 'bin/measure-chunk-stats.sh'),
-        'utf-8',
-      );
-      expect(script).toContain('.next/dev');
-    });
-  });
-
-  describe('custom server webpack option', () => {
-    it('should pass webpack: true to next() in the custom server', () => {
-      const crowiIndex = readFileSync(
-        path.join(appDir, 'src/server/crowi/index.ts'),
-        'utf-8',
-      );
-      // The programmatic API should use webpack: true to opt out of Turbopack
-      expect(crowiIndex).toMatch(/next\(\{[^}]*webpack:\s*true/);
-    });
-  });
-
-  describe('next.config.ts webpack function', () => {
-    it('should have a webpack function defined', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toMatch(/webpack\(config,\s*options\)/);
-    });
-
-    it('should have all 7 null-loader rules', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      const nullLoaderPackages = [
-        'dtrace-provider',
-        'mongoose',
-        'mathjax-full',
-        'i18next-fs-backend',
-        'bunyan',
-        'bunyan-format',
-        'core-js',
-      ];
-      for (const pkg of nullLoaderPackages) {
-        expect(config).toContain(pkg);
-      }
-      expect(config).toContain("use: 'null-loader'");
-    });
-
-    it('should have superjson-ssr-loader', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('superjson-ssr-loader');
-    });
-
-    it('should have I18NextHMRPlugin', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('I18NextHMRPlugin');
-    });
-
-    it('should have ChunkModuleStatsPlugin', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('createChunkModuleStatsPlugin');
-    });
-
-    it('should have source-map-loader', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('source-map-loader');
-    });
-
-    it('should have bundlePagesRouterDependencies enabled', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('bundlePagesRouterDependencies: true');
-    });
-
-    it('should have optimizePackageImports configured', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('optimizePackageImports');
-    });
-
-    it('should have transpilePackages configured', () => {
-      const config = readFileSync(path.join(appDir, 'next.config.ts'), 'utf-8');
-      expect(config).toContain('transpilePackages');
-    });
-  });
-});