Skip to content

perf(cli): single-process dev server with Rust CLI wrapper - #87941

Closed
feedthejim wants to merge 2 commits into
perf-dev-bytecode-cachefrom
perf-dev-rust-cli
Closed

feedthejim wants to merge 2 commits into
perf-dev-bytecode-cachefrom
perf-dev-rust-cli

Conversation

@feedthejim

@feedthejim feedthejim commented Dec 30, 2025 •

Copy link
Copy Markdown
Contributor

Summary

Replace fork-based architecture with single Node.js process:

  • Add Rust CLI wrapper (crates/next-cli/) that handles:

    • NODE_OPTIONS (memory limits, source maps, inspect)
    • WATCHPACK_WATCHER_LIMIT for large projects
    • Restart loop (exit code 77) without Node.js overhead
  • Simplify next-dev.ts: remove fork(), IPC, child process management

  • Simplify start-server.ts: remove NEXT_PRIVATE_WORKER handling

  • Add shell entry points (bin/next, bin/next.cmd) that:

    • Use native binary when available (~10ms overhead)
    • Fall back to Node.js entry point
  • Cross-platform release pipeline for 8 platforms

Test Fixes

The Rust CLI introduces exit code 77 for server restarts (e.g., after config file changes). Tests run Node.js directly without the Rust wrapper, so:

  • test/lib/next-modes/next-dev.ts - Add RESTART_EXIT_CODE = 77 handling in NextDevInstance.start() to auto-restart the server when it exits with code 77, mimicking the Rust wrapper behavior

Benchmark Results (cumulative)

Metric Before After Improvement
Cold Listen 255ms 190ms -65ms
Cold Ready 1085ms 951ms -134ms
Warm Listen 260ms 185ms -75ms
Warm Ready 602ms 483ms -119ms

Biggest impact of all optimizations - eliminates Node.js fork overhead.

Test Plan

  • Run benchmark script to verify improvements
  • Build passes (including Rust CLI)
  • Dev server starts correctly with native binary
  • Fallback to Node.js works when native binary unavailable
  • Config file change restart works in tests (exit code 77 handling)

feedthejim commented Dec 30, 2025 •

Copy link
Copy Markdown
Contributor Author

@nextjs-bot

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 5aa417dd1410558cfcca7b1766224594e183b103

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

1 similar comment
@nextjs-bot

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 5aa417dd1410558cfcca7b1766224594e183b103

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch 2 times, most recently from 96f6373 to f196646 Compare December 30, 2025 14:07
@feedthejim
feedthejim force-pushed the perf-dev-rust-cli branch 2 times, most recently from 5541176 to e359b2b Compare December 30, 2025 14:11
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from f196646 to 58e04f1 Compare December 30, 2025 14:11
Comment thread crates/next-cli/src/main.rs Outdated
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from 58e04f1 to 7afaf27 Compare December 30, 2025 14:30
@codspeed

codspeed Bot commented Dec 30, 2025 •

Copy link
Copy Markdown

CodSpeed Performance Report

Merging #87941 will not alter performance

Comparing perf-dev-rust-cli (f02153b) with canary (40a4c11)1

Summary

✅ 17 untouched
⏩ 3 skipped2

Footnotes

  1. No successful run was found on perf-dev-bytecode-cache (b556af7) during the generation of this report, so canary (40a4c11) was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

  2. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Comment thread packages/next/bin/next Outdated
Comment on lines +263 to +266
await startServer({
...devServerOptions,
selfSignedCertificate: certificate,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dev server no longer supports auto-restart when configuration files change if the Rust CLI wrapper is not available. When startServer() detects a config change, it calls process.exit(77), expecting a Rust wrapper to catch this exit code and restart the process. However, when falling back to the Node.js entry point, this exit code is not handled, causing the dev server to exit permanently instead of restarting.

View Details
📝 Patch Details
diff --git a/packages/next/bin/next b/packages/next/bin/next
index 84eee1314d..816b148735 100755
--- a/packages/next/bin/next
+++ b/packages/next/bin/next
@@ -10,4 +10,7 @@ for pkg in "$DIR"/../node_modules/@next/cli-*/next; do
 done
 
 # Fallback: Node.js
+# Set NEXT_CLI_NO_RUST to indicate we're running without the Rust wrapper.
+# This allows the Node.js dev server to implement its own restart loop for config changes.
+export NEXT_CLI_NO_RUST=1
 exec node "$DIR/../dist/bin/next" "$@"
diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts
index 16eadfa57c..49838014b7 100644
--- a/packages/next/src/cli/next-dev.ts
+++ b/packages/next/src/cli/next-dev.ts
@@ -228,10 +228,36 @@ const nextDev = async (
     process.env.TURBOPACK = '1'
   }
 
+  // Track if we're in a restart loop to handle exit code 77
+  // Only needed when running without the Rust CLI wrapper (NEXT_CLI_NO_RUST)
+  let restartExitCode: number | null = null
+  const originalExit = process.exit
+  const noRustWrapper = process.env.NEXT_CLI_NO_RUST === '1'
+
+  // Intercept process.exit to handle restart signals from the dev server
+  // When startServer() detects a config file change, it calls process.exit(77)
+  // The Rust wrapper catches this and restarts, but when running directly with Node.js,
+  // we need to handle it here to maintain dev server functionality
+  if (noRustWrapper) {
+    process.exit = function (code?: number | string) {
+      const exitCode = typeof code === 'string' ? parseInt(code, 10) : code || 0
+      // Exit code 77 signals "restart needed" (config file changed)
+      if (exitCode === 77) {
+        restartExitCode = 77
+        // Don't actually exit - we'll loop back and restart
+        return undefined
+      }
+      // For all other exit codes, actually exit the process
+      return originalExit.call(process, code as never)
+    } as any
+  }
+
   const runDevServer = async (reboot: boolean) => {
     try {
       // Load startServer from bundled version with optional bytecode caching
-      // The Rust wrapper handles restarts via exit code 77
+      // The Rust wrapper handles restarts via exit code 77.
+      // When running without the Rust wrapper, the intercepted process.exit() above
+      // will catch exit code 77 and trigger a restart.
       const { startServer } =
         require('../server/lib/start-server-with-cache') as typeof import('../server/lib/start-server-with-cache')
 
@@ -272,7 +298,29 @@ const nextDev = async (
     }
   }
 
-  await runDevServer(false)
+  // Restart loop for the Node.js fallback path
+  // When NEXT_CLI_NO_RUST=1 (running without the Rust CLI wrapper),
+  // we need to handle config file changes and restart the server.
+  // The Rust wrapper has its own restart loop and won't reach this code.
+  if (noRustWrapper) {
+    let isRestart = false
+    while (true) {
+      restartExitCode = null
+      await runDevServer(isRestart)
+
+      // If startServer exited with code 77, loop back and restart
+      if (restartExitCode === 77) {
+        isRestart = true
+        continue
+      }
+      // Otherwise, exit normally
+      break
+    }
+  } else {
+    // When running with the Rust CLI wrapper, it handles the restart loop.
+    // We just run the server once and exit (potentially with code 77 for restarts).
+    await runDevServer(false)
+  }
 }
 
 export { nextDev }

Analysis

Dev server exits permanently when configuration files change in Node.js fallback mode

What fails: When running next dev without the Rust CLI wrapper (@next/cli-* binary), the dev server exits with code 77 and does not restart when configuration files change (next.config.js, next.config.mjs, tsconfig.json, etc.)

How to reproduce:

  1. Run Next.js dev server directly through the Node.js fallback path (bypassing the Rust wrapper):

    # Simulate the fallback: directly run Node.js instead of using the shell wrapper
    node packages/next/dist/bin/next dev
  2. Modify a config file that triggers the watcher (e.g., next.config.js, tsconfig.json)

  3. Observe: The dev server logs "Found a change in [filename]. Restarting the server to apply the changes..." then exits with code 77

  4. The dev server does not restart - it remains stopped

Expected behavior: The dev server should detect the config file change and restart the server, just like it does when using the Rust wrapper

Root cause: The architecture change in commit 88f651876c moved restart loop logic from JavaScript to a Rust wrapper. The Rust binary correctly handles exit code 77 by looping and restarting the Node.js process. However, when the Rust binary is unavailable and the shell script (packages/next/bin/next) falls back to directly executing node, there is no restart loop in the Node.js code to handle the exit code 77 signal from the config file watcher in start-server.ts.

Affected scenarios:

  • Developers on unsupported platforms where Rust binaries aren't available
  • Direct invocation of Node.js entry point (bypassing the shell script)
  • Failed or missing binary installation (fallback to Node.js)
  • Development environments where the Rust wrapper is not available

References:

  • Entry point: packages/next/bin/next (shell script)
  • Node.js entry point: packages/next/src/bin/next.ts
  • Dev CLI: packages/next/src/cli/next-dev.ts
  • Config file watcher: packages/next/src/server/lib/start-server.ts (line 518)
  • Exit code constant: packages/next/src/server/lib/utils.ts (RESTART_EXIT_CODE = 77)
  • Rust wrapper: crates/next-cli/src/main.rs (handles restart loop)

@feedthejim
feedthejim changed the base branch from perf-dev-webpack-bundling to graphite-base/87941 December 30, 2025 14:45
@feedthejim
feedthejim changed the base branch from graphite-base/87941 to perf-dev-webpack-bundling December 30, 2025 14:46
@feedthejim
feedthejim force-pushed the perf-dev-rust-cli branch 2 times, most recently from 5b4ce7a to 9bc50a8 Compare December 30, 2025 14:55
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from 39cecfc to 2258256 Compare December 30, 2025 14:55
@feedthejim
feedthejim changed the base branch from perf-dev-webpack-bundling to graphite-base/87941 January 2, 2026 09:52
@feedthejim
feedthejim force-pushed the graphite-base/87941 branch from e1e06f4 to 55e1a6e Compare January 2, 2026 10:35
@feedthejim
feedthejim changed the base branch from graphite-base/87941 to perf-dev-webpack-bundling January 2, 2026 10:35
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch 2 times, most recently from 4daf945 to b52b486 Compare January 2, 2026 10:59
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from b52b486 to 9e92ac3 Compare January 2, 2026 11:08
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from 9e92ac3 to d1c86ed Compare January 2, 2026 11:22
@feedthejim
feedthejim force-pushed the perf-dev-rust-cli branch 2 times, most recently from bbecde9 to ce856be Compare January 2, 2026 11:25
@feedthejim
feedthejim force-pushed the perf-dev-webpack-bundling branch from d1c86ed to 763fecf Compare January 2, 2026 11:25
@feedthejim
feedthejim changed the base branch from perf-dev-webpack-bundling to graphite-base/87941 January 2, 2026 11:41
@feedthejim
feedthejim force-pushed the graphite-base/87941 branch from 763fecf to 28f621b Compare January 2, 2026 11:50
@nextjs-bot

nextjs-bot commented Jan 2, 2026 •

Copy link
Copy Markdown
Contributor

✅ Tests Passed

Updated 2026-01-02 14:55:58 UTC · Commit: 86ee4c8

}

const runDevServer = async (reboot: boolean) => {
try {
// Load startServer from bundled version with optional bytecode caching

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactored dev server code removes trace upload logic that was previously executed when the server needed to restart due to config file changes. This causes telemetry traces to be lost on restart.

View Details
📝 Patch Details
diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts
index a569440fd7..0928805148 100644
--- a/packages/next/src/cli/next-dev.ts
+++ b/packages/next/src/cli/next-dev.ts
@@ -222,6 +222,25 @@ const nextDev = async (
     traceUploadUrl = options.experimentalUploadTrace
   }
 
+  // Handle config change restart (exit code 77) with trace upload
+  // When config files change, start-server.ts will exit with code 77.
+  // We need to upload traces before the process restarts to preserve metrics.
+  const RESTART_EXIT_CODE = 77
+  process.on('exit', (code) => {
+    if (code === RESTART_EXIT_CODE && traceUploadUrl && config) {
+      const uploadTrace = (
+        require('../trace/upload-trace') as typeof import('../trace/upload-trace')
+      ).default
+      uploadTrace({
+        traceUploadUrl,
+        mode: 'dev',
+        projectDir: dir,
+        distDir: config.distDir,
+        isTurboSession: bundler === Bundler.Turbopack,
+      })
+    }
+  })
+
   const devServerOptions: StartServerOptions = {
     dir,
     port,
diff --git a/packages/next/src/next-devtools/server/restart-dev-server-middleware.ts b/packages/next/src/next-devtools/server/restart-dev-server-middleware.ts
index f410e3a4a3..39fb9b7c3d 100644
--- a/packages/next/src/next-devtools/server/restart-dev-server-middleware.ts
+++ b/packages/next/src/next-devtools/server/restart-dev-server-middleware.ts
@@ -1,6 +1,7 @@
 import type { ServerResponse, IncomingMessage } from 'http'
 import type { Telemetry } from '../../telemetry/storage'
 import { RESTART_EXIT_CODE } from '../../server/lib/utils'
+import { flushAllTraces } from '../../trace'
 import { middlewareResponse } from './middleware-response'
 import type { Project } from '../../build/swc/types'
 import { invalidateFileSystemCache as invalidateWebpackFileSystemCache } from '../../build/webpack/cache-invalidation'
@@ -60,6 +61,8 @@ export function getRestartDevServerMiddleware({
 
     // TODO: Use flushDetached
     await telemetry.flush()
+    // Flush trace events before restarting to ensure they are persisted
+    await flushAllTraces({ end: false })
 
     // do this async to try to give the response a chance to send
     // it's not really important if it doesn't though
diff --git a/packages/next/src/server/lib/start-server.ts b/packages/next/src/server/lib/start-server.ts
index bb7e737cf4..31d2b4cb48 100644
--- a/packages/next/src/server/lib/start-server.ts
+++ b/packages/next/src/server/lib/start-server.ts
@@ -515,6 +515,9 @@ export async function startServer(
           filename
         )}. Restarting the server to apply the changes...`
       )
+      // Flush all trace events before restarting to ensure they are persisted
+      // The parent process will handle uploading traces if needed
+      await flushAllTraces({ end: false })
       process.exit(RESTART_EXIT_CODE)
     })
   }
diff --git a/test-trace b/test-trace
new file mode 100644
index 0000000000..20083a5dd2
--- /dev/null
+++ b/test-trace
@@ -0,0 +1 @@
+{"event":"config-change"}

Analysis

Telemetry traces lost on dev server restart with config file changes

What fails: When Next.js dev server restarts due to config file changes (e.g., next.config.js modification), telemetry trace data is lost even with --experimental-upload-trace flag enabled.

How to reproduce:

  1. Start Next.js dev server with trace uploads enabled: next dev --experimental-upload-trace=<url>
  2. Generate some trace events (page visits, builds)
  3. Modify next.config.js
  4. Observe server restarts
  5. Check if traces are uploaded - they will not be because they were never flushed/uploaded before restart

What happens: When config files are detected as changed, packages/next/src/server/lib/start-server.ts (line 518) calls process.exit(77) to signal a restart. This exit happens before:

  • Buffered trace events are flushed to disk
  • Traces are uploaded via the telemetry service

The new single-process architecture (introduced in commit 09cbf1b85c) removed the parent process's restart handler that previously uploaded traces before restarting.

Expected behavior: Trace events should be flushed and uploaded before the process exits with restart code 77, preserving observability across dev server restarts.

Root cause: The refactoring to single-process dev server with Rust CLI wrapper removed the explicit trace upload logic that was previously in the child process exit handler. Trace events are buffered in memory (batched every 100 events), and without flushing, events < 100 are lost when the process exits.

References:

  • Old code (before commit 09cbf1b85c): Had child.on('exit', async (code, signal) => { if (code === RESTART_EXIT_CODE) { uploadTrace(...); return startServer(...); } }
  • Trace batching: packages/next/src/trace/report/to-json.ts line 10-17
  • Restart code constant: packages/next/src/server/lib/utils.ts line 257

Comment on lines +80 to +85
/// Check if user has already set memory limit in NODE_OPTIONS
fn user_set_memory_limit() -> bool {
env::var("NODE_OPTIONS")
.map(|opts| opts.contains("max-old-space-size") || opts.contains("max_old_space_size"))
.unwrap_or(false)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Rust CLI wrapper doesn't handle conflicts between NODE_OPTIONS inspect flags and command-line --inspect flags. If NODE_OPTIONS contains an inspect flag and the user also passes --inspect on the command line, Node.js will error due to conflicting debug ports.

View Details
📝 Patch Details
diff --git a/crates/next-cli/src/main.rs b/crates/next-cli/src/main.rs
index e00084b6ec..df11bced6c 100644
--- a/crates/next-cli/src/main.rs
+++ b/crates/next-cli/src/main.rs
@@ -84,6 +84,38 @@ fn user_set_memory_limit() -> bool {
         .unwrap_or(false)
 }
 
+/// Remove inspect flags from NODE_OPTIONS string
+/// This is extracted to a separate function for testing
+fn filter_node_options_inspect(node_options: &str) -> Option<String> {
+    let cleaned = node_options
+        .split_whitespace()
+        .filter(|arg| {
+            !arg.starts_with("--inspect=") &&
+            !arg.starts_with("--inspect-brk") &&
+            !arg.starts_with("--inspect_brk") &&
+            *arg != "--inspect"
+        })
+        .collect::<Vec<_>>()
+        .join(" ");
+    
+    if cleaned.is_empty() {
+        None
+    } else {
+        Some(cleaned)
+    }
+}
+
+/// Remove inspect flags from NODE_OPTIONS to avoid conflicts with command-line flags
+fn remove_inspect_from_node_options() {
+    if let Ok(node_options) = env::var("NODE_OPTIONS") {
+        if let Some(cleaned) = filter_node_options_inspect(&node_options) {
+            env::set_var("NODE_OPTIONS", cleaned);
+        } else {
+            env::remove_var("NODE_OPTIONS");
+        }
+    }
+}
+
 /// Get total system memory in MB
 fn get_system_memory_mb() -> Option<u64> {
     #[cfg(target_os = "linux")]
@@ -128,6 +160,12 @@ fn main() {
     let parsed = parse_args(&args);
     let script = get_script_path();
 
+    // If user provided inspect flags on command line, remove any from NODE_OPTIONS
+    // to avoid conflicts (e.g., NODE_OPTIONS='--inspect' and --inspect=9229 both trying to use port 9229)
+    if !parsed.node_flags.is_empty() {
+        remove_inspect_from_node_options();
+    }
+
     // Calculate memory limit once (50% of system memory)
     let memory_limit_mb = get_system_memory_mb().map(|mb| mb / 2);
     let should_set_memory = memory_limit_mb.is_some() && !user_set_memory_limit();
@@ -251,4 +289,46 @@ mod tests {
         let content = "SomeOther: 12345 kB\n";
         assert_eq!(parse_linux_meminfo(content), None);
     }
+
+    #[test]
+    fn test_filter_node_options_inspect_removes_inspect() {
+        assert_eq!(filter_node_options_inspect("--inspect"), None);
+        assert_eq!(filter_node_options_inspect("--inspect=9229"), None);
+        assert_eq!(filter_node_options_inspect("--inspect-brk"), None);
+        assert_eq!(filter_node_options_inspect("--inspect-brk=9229"), None);
+        assert_eq!(filter_node_options_inspect("--inspect_brk"), None);
+        assert_eq!(filter_node_options_inspect("--inspect_brk=9229"), None);
+    }
+
+    #[test]
+    fn test_filter_node_options_inspect_keeps_other_options() {
+        assert_eq!(
+            filter_node_options_inspect("--max-old-space-size=4096"),
+            Some("--max-old-space-size=4096".to_string())
+        );
+        assert_eq!(
+            filter_node_options_inspect("--max-old-space-size=4096 --inspect=9229"),
+            Some("--max-old-space-size=4096".to_string())
+        );
+        assert_eq!(
+            filter_node_options_inspect("--inspect --max-old-space-size=4096"),
+            Some("--max-old-space-size=4096".to_string())
+        );
+        assert_eq!(
+            filter_node_options_inspect("--experimental-foo --inspect-brk=9229 --bar=baz"),
+            Some("--experimental-foo --bar=baz".to_string())
+        );
+    }
+
+    #[test]
+    fn test_filter_node_options_inspect_multiple() {
+        assert_eq!(
+            filter_node_options_inspect("--inspect --inspect-brk"),
+            None
+        );
+        assert_eq!(
+            filter_node_options_inspect("--max-old-space-size=4096 --inspect --inspect-brk"),
+            Some("--max-old-space-size=4096".to_string())
+        );
+    }
 }

Analysis

Conflicting Node.js inspect flags not handled in Rust CLI wrapper

What fails: The Rust CLI wrapper (crates/next-cli/src/main.rs) extracts --inspect* flags from command-line arguments and passes them to Node.js without removing conflicting inspect flags from the inherited NODE_OPTIONS environment variable. When NODE_OPTIONS contains --inspect or --inspect-brk and the user also passes --inspect on the command line, Node.js errors with "Starting inspector on 127.0.0.1:9229 failed: address already in use".

How to reproduce:

# Set up developer environment with global debug inspection
export NODE_OPTIONS="--inspect"

# User runs next dev with explicit inspect flag
next dev --inspect=9229

Result: Node.js startup fails with error:

Starting inspector on 127.0.0.1:9229 failed: address already in use

Expected: The Rust CLI should detect the command-line --inspect flag and remove conflicting inspect flags from NODE_OPTIONS before spawning Node.js, allowing both flag sources to coexist without conflict. This behavior was present in the JavaScript implementation (commit 4e27b90) which used getNodeOptionsWithoutInspect() to filter NODE_OPTIONS when inspect was provided.

Root cause: The Rust CLI is a separate compiled binary that doesn't use the JavaScript utility functions. It needs its own implementation to handle this conflict, similar to the old JavaScript code: https://github.com/vercel/next.js/commit/4e27b908fe

@nextjs-bot

nextjs-bot commented Jan 2, 2026 •

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 85820c5f66dda67cf27fc80e72cba55ea4351ee5

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

Comment thread packages/next/bin/next
Comment on lines +47 to +50
const child = spawn(
process.execPath,
[...nodeArgs, ...inspectArgs, scriptPath, ...scriptArgs],
{ stdio: 'inherit', env: process.env }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const child = spawn(
process.execPath,
[...nodeArgs, ...inspectArgs, scriptPath, ...scriptArgs],
{ stdio: 'inherit', env: process.env }
// Set up environment with optimizations matching the Rust binary
const env = { ...process.env }
// macOS: limit file watchers to avoid slow cleanup
// https://github.com/nodejs/node/issues/29949
if (platform === 'darwin' && scriptArgs[0] === 'dev') {
env.WATCHPACK_WATCHER_LIMIT = '20'
}
const child = spawn(
process.execPath,
[...nodeArgs, ...inspectArgs, scriptPath, ...scriptArgs],
{ stdio: 'inherit', env }

The Node.js fallback doesn't set WATCHPACK_WATCHER_LIMIT on macOS, causing the file watcher optimization to be skipped when the Rust binary isn't available.

View Details

Analysis

Missing WATCHPACK_WATCHER_LIMIT optimization in Node.js fallback on macOS

What fails: The runWithNode() fallback in packages/next/bin/next doesn't set the WATCHPACK_WATCHER_LIMIT environment variable on macOS when running the dev command, causing file watcher performance degradation

How to reproduce:

  1. On macOS, ensure the Rust CLI binary is not available (delete the @next/cli-* package or simulate unavailability)
  2. Run next dev
  3. The fallback path in packages/next/bin/next is used instead of the Rust binary

Result: The WATCHPACK_WATCHER_LIMIT environment variable is not set, causing slower file watcher cleanup on macOS

Expected: Should match the Rust CLI behavior (crates/next-cli/src/main.rs, lines 158-161) which sets WATCHPACK_WATCHER_LIMIT=20 on macOS when running dev

Context: The watchpack v2.0.0 release introduced a limit on the maximum number of OS watchers (2000 on macOS). The WATCHPACK_WATCHER_LIMIT environment variable allows tuning this limit to optimize performance. The Rust binary correctly applies this optimization on macOS, but the Node.js fallback was missing this logic, causing inconsistent behavior between the two execution paths.

@nextjs-bot

nextjs-bot commented Jan 2, 2026 •

Copy link
Copy Markdown
Contributor

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
buildDuration 18.2s 15.1s N/A
buildDurationCached 13.8s 10.9s N/A
nodeModulesSize 457 MB 463 MB ⚠️ +5.98 MB
nextStartRea..uration (ms) 718ms 750ms N/A
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
2086.HASH.js gzip 169 B 169 B ✓
2161-HASH.js gzip 5.39 kB 5.41 kB N/A
2747-HASH.js gzip 4.48 kB 4.46 kB N/A
4322-HASH.js gzip 51.2 kB 51 kB N/A
ec793fe8-HASH.js gzip 62.3 kB 62.3 kB N/A
framework-HASH.js gzip 59.8 kB 59.8 kB N/A
main-app-HASH.js gzip 251 B 254 B N/A
main-HASH.js gzip 38.4 kB 38.7 kB ⚠️ +368 B
webpack-HASH.js gzip 1.68 kB 1.69 kB N/A
Overall change 38.5 kB 38.9 kB ⚠️ +368 B
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB ✓
Overall change 39.4 kB 39.4 kB ✓
Client Pages
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
_app-HASH.js gzip 194 B 193 B N/A
_error-HASH.js gzip 182 B 182 B ✓
css-HASH.js gzip 336 B 335 B N/A
dynamic-HASH.js gzip 1.8 kB 1.8 kB N/A
edge-ssr-HASH.js gzip 256 B 256 B ✓
head-HASH.js gzip 352 B 349 B N/A
hooks-HASH.js gzip 385 B 384 B N/A
image-HASH.js gzip 580 B 580 B ✓
index-HASH.js gzip 259 B 258 B N/A
link-HASH.js gzip 2.5 kB 2.51 kB N/A
routerDirect..HASH.js gzip 319 B 317 B N/A
script-HASH.js gzip 385 B 387 B N/A
withRouter-HASH.js gzip 316 B 315 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B ✓
Overall change 1.12 kB 1.12 kB ✓
Client Build Manifests
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
_buildManifest.js gzip 738 B 738 B ✓
Overall change 738 B 738 B ✓
Rendered Page Sizes
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
index.html gzip 523 B 525 B N/A
link.html gzip 537 B 539 B N/A
withRouter.html gzip 520 B 521 B N/A
Overall change 0 B 0 B ✓
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
edge-ssr.js gzip 124 kB 124 kB N/A
page.js gzip 237 kB 238 kB ⚠️ +606 B
Overall change 237 kB 238 kB ⚠️ +606 B
Middleware size Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
middleware-b..fest.js gzip 652 B 655 B N/A
middleware-r..fest.js gzip 155 B 156 B N/A
middleware.js gzip 32.8 kB 32.9 kB ⚠️ +121 B
edge-runtime..pack.js gzip 846 B 846 B ✓
Overall change 33.6 kB 33.7 kB ⚠️ +121 B
Next Runtimes Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
app-page-exp...dev.js gzip 301 kB 302 kB ⚠️ +230 B
app-page-exp..prod.js gzip 156 kB 156 kB ✓
app-page-tur...dev.js gzip 301 kB 301 kB ⚠️ +235 B
app-page-tur..prod.js gzip 156 kB 156 kB ✓
app-page-tur...dev.js gzip 298 kB 298 kB ⚠️ +244 B
app-page-tur..prod.js gzip 154 kB 154 kB ✓
app-page.run...dev.js gzip 298 kB 298 kB ⚠️ +237 B
app-page.run..prod.js gzip 154 kB 154 kB ✓
app-route-ex...dev.js gzip 68.7 kB 68.7 kB ✓
app-route-ex..prod.js gzip 47.5 kB 47.5 kB ✓
app-route-tu...dev.js gzip 68.7 kB 68.7 kB ✓
app-route-tu..prod.js gzip 47.5 kB 47.5 kB ✓
app-route-tu...dev.js gzip 68.3 kB 68.3 kB ✓
app-route-tu..prod.js gzip 47.3 kB 47.3 kB ✓
app-route.ru...dev.js gzip 68.3 kB 68.3 kB ✓
app-route.ru..prod.js gzip 47.3 kB 47.3 kB ✓
dist_client_...dev.js gzip 324 B 324 B ✓
dist_client_...dev.js gzip 326 B 326 B ✓
dist_client_...dev.js gzip 318 B 318 B ✓
dist_client_...dev.js gzip 317 B 317 B ✓
pages-api-tu...dev.js gzip 41.1 kB 41.1 kB ✓
pages-api-tu..prod.js gzip 31.2 kB 31.2 kB ✓
pages-api.ru...dev.js gzip 41 kB 41 kB ✓
pages-api.ru..prod.js gzip 31.2 kB 31.2 kB ✓
pages-turbo....dev.js gzip 50.7 kB 50.7 kB ✓
pages-turbo...prod.js gzip 38.2 kB 38.2 kB ✓
pages.runtim...dev.js gzip 50.6 kB 50.6 kB ✓
pages.runtim..prod.js gzip 38.1 kB 38.1 kB ✓
server.runti..prod.js gzip 59.9 kB 60.3 kB ⚠️ +394 B
Overall change 2.67 MB 2.67 MB ⚠️ +1.34 kB
build cache Overall increase ⚠️
vercel/next.js canary vercel/next.js perf-dev-rust-cli Change
0.pack gzip 3.61 MB 3.61 MB ⚠️ +5.74 kB
index.pack gzip 98.1 kB 97.8 kB N/A
Overall change 3.61 MB 3.61 MB ⚠️ +5.74 kB
Diff details
Diff for page.js

Diff too large to display

Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for _buildManifest.js
@@ -611,35 +611,35 @@ self.__BUILD_MANIFEST = (function (a, b, c) {
       numHashes: NaN,
       bitArray: [],
     },
-    "/": ["static\u002Fchunks\u002Fpages\u002Findex-dda7d8b64d4ba15c.js"],
+    "/": ["static\u002Fchunks\u002Fpages\u002Findex-d95f7ec6af4d2644.js"],
     "/_error": [
-      "static\u002Fchunks\u002Fpages\u002F_error-6ef44d3954f25711.js",
+      "static\u002Fchunks\u002Fpages\u002F_error-209c0c82205a7c9f.js",
     ],
     "/css": [
       "static\u002Fcss\u002Fded6b86ab9cc0a1f.css",
-      "static\u002Fchunks\u002Fpages\u002Fcss-c8aaa7211416a045.js",
+      "static\u002Fchunks\u002Fpages\u002Fcss-62710339bc830ded.js",
     ],
     "/dynamic": [
-      "static\u002Fchunks\u002Fpages\u002Fdynamic-d53bb7f318f342c2.js",
+      "static\u002Fchunks\u002Fpages\u002Fdynamic-8ed3486bb68dd6c6.js",
     ],
     "/edge-ssr": [
-      "static\u002Fchunks\u002Fpages\u002Fedge-ssr-1383106d4a3e7d72.js",
+      "static\u002Fchunks\u002Fpages\u002Fedge-ssr-0db9f7bb610d3072.js",
     ],
-    "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-1db1c4be1a45662f.js"],
-    "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-e6deee5b72a5b112.js"],
+    "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-747416c4075e42aa.js"],
+    "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-8b10205be7505244.js"],
     "/image": [
-      "static\u002Fchunks\u002F2747-90c828280005c0c3.js",
-      "static\u002Fchunks\u002Fpages\u002Fimage-868472cd5e84efc9.js",
+      "static\u002Fchunks\u002F6349-ee9aecde860d4832.js",
+      "static\u002Fchunks\u002Fpages\u002Fimage-f90ae17c3ad1d38b.js",
     ],
-    "/link": ["static\u002Fchunks\u002Fpages\u002Flink-0ec374e48b2ce5d9.js"],
+    "/link": ["static\u002Fchunks\u002Fpages\u002Flink-6ab9a67b348df1bf.js"],
     "/routerDirect": [
-      "static\u002Fchunks\u002Fpages\u002FrouterDirect-1a34bfadbc088491.js",
+      "static\u002Fchunks\u002Fpages\u002FrouterDirect-f132fb471e65a8b9.js",
     ],
     "/script": [
-      "static\u002Fchunks\u002Fpages\u002Fscript-52320f59afbd096d.js",
+      "static\u002Fchunks\u002Fpages\u002Fscript-8e2028e44b8da2d9.js",
     ],
     "/withRouter": [
-      "static\u002Fchunks\u002Fpages\u002FwithRouter-3b1a16d3dfa21c16.js",
+      "static\u002Fchunks\u002Fpages\u002FwithRouter-a86d259faa012c1c.js",
     ],
     sortedPages: [
       "\u002F",
Diff for dynamic-HASH.js
@@ -1,17 +1,24 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2291],
   {
-    /***/ 1231: /***/ (
-      module,
+    /***/ 2604: /***/ (
+      __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
-      module.exports = __webpack_require__(4464);
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/dynamic",
+        function () {
+          return __webpack_require__(3643);
+        },
+      ]);
+      if (false) {
+      }
 
       /***/
     },
 
-    /***/ 2101: /***/ (
+    /***/ 3401: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -38,24 +45,63 @@
       /***/
     },
 
-    /***/ 3458: /***/ (
+    /***/ 3643: /***/ (
       __unused_webpack_module,
-      __unused_webpack_exports,
+      __webpack_exports__,
       __webpack_require__
     ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/dynamic",
-        function () {
-          return __webpack_require__(9381);
-        },
-      ]);
-      if (false) {
-      }
+      "use strict";
+      __webpack_require__.r(__webpack_exports__);
+      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+        /* harmony export */
+      });
+      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+        __webpack_require__(3108);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
+        __webpack_require__(3973);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
+        /*#__PURE__*/ __webpack_require__.n(
+          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
+        );
+
+      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
+        () =>
+          __webpack_require__
+            .e(/* import() */ 1596)
+            .then(__webpack_require__.bind(__webpack_require__, 1596))
+            .then((mod) => mod.Hello),
+        {
+          loadableGenerated: {
+            webpack: () => [/*require.resolve*/ 1596],
+          },
+        }
+      );
+      const Page = () =>
+        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
+          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
+          {
+            children: [
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
+                children: "testing next/dynamic size",
+              }),
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+                DynamicHello,
+                {}
+              ),
+            ],
+          }
+        );
+      var __N_SSP = true;
+      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
 
       /***/
     },
 
-    /***/ 4464: /***/ (module, exports, __webpack_require__) => {
+    /***/ 3660: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -88,7 +134,7 @@
         __webpack_require__(4312)
       );
       const _loadablesharedruntime = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(9799)
+        __webpack_require__(4539)
       );
       const isServerSide = "object" === "undefined";
       // Normalize loader to return the module as form { default: Component } for `React.lazy`.
@@ -188,63 +234,17 @@
       /***/
     },
 
-    /***/ 9381: /***/ (
-      __unused_webpack_module,
-      __webpack_exports__,
+    /***/ 3973: /***/ (
+      module,
+      __unused_webpack_exports,
       __webpack_require__
     ) => {
-      "use strict";
-      __webpack_require__.r(__webpack_exports__);
-      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
-        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
-        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
-        /* harmony export */
-      });
-      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(3108);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(1231);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
-        /*#__PURE__*/ __webpack_require__.n(
-          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
-        );
-
-      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
-        () =>
-          __webpack_require__
-            .e(/* import() */ 2086)
-            .then(__webpack_require__.bind(__webpack_require__, 2086))
-            .then((mod) => mod.Hello),
-        {
-          loadableGenerated: {
-            webpack: () => [/*require.resolve*/ 2086],
-          },
-        }
-      );
-      const Page = () =>
-        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
-          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
-          {
-            children: [
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
-                children: "testing next/dynamic size",
-              }),
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
-                DynamicHello,
-                {}
-              ),
-            ],
-          }
-        );
-      var __N_SSP = true;
-      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
+      module.exports = __webpack_require__(3660);
 
       /***/
     },
 
-    /***/ 9799: /***/ (
+    /***/ 4539: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -286,7 +286,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(4312)
       );
-      const _loadablecontextsharedruntime = __webpack_require__(2101);
+      const _loadablecontextsharedruntime = __webpack_require__(3401);
       function resolve(obj) {
         return obj && obj.default ? obj.default : obj;
       }
@@ -524,7 +524,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(3458)
+      __webpack_exec__(2604)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for edge-ssr-HASH.js
@@ -1,7 +1,24 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [676],
   {
-    /***/ 1564: /***/ (
+    /***/ 7046: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/edge-ssr",
+        function () {
+          return __webpack_require__(7142);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 7142: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -20,30 +37,13 @@
 
       /***/
     },
-
-    /***/ 4300: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/edge-ssr",
-        function () {
-          return __webpack_require__(1564);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(4300)
+      __webpack_exec__(7046)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for head-HASH.js
@@ -1,34 +1,17 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [5350],
   {
-    /***/ 1548: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/head",
-        function () {
-          return __webpack_require__(4282);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 3952: /***/ (
+    /***/ 3770: /***/ (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
-      module.exports = __webpack_require__(1653);
+      module.exports = __webpack_require__(9025);
 
       /***/
     },
 
-    /***/ 4282: /***/ (
+    /***/ 6236: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -43,7 +26,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(3108);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(3952);
+        __webpack_require__(3770);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_head__WEBPACK_IMPORTED_MODULE_1__
@@ -76,13 +59,30 @@
 
       /***/
     },
+
+    /***/ 6510: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/head",
+        function () {
+          return __webpack_require__(6236);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1548)
+      __webpack_exec__(6510)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for index-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [3332],
   {
-    /***/ 6376: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/",
-        function () {
-          return __webpack_require__(8460);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 8460: /***/ (
+    /***/ 830: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -36,13 +19,30 @@
 
       /***/
     },
+
+    /***/ 1938: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/",
+        function () {
+          return __webpack_require__(830);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(6376)
+      __webpack_exec__(1938)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for link-HASH.js
@@ -1,7 +1,220 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [4672],
   {
-    /***/ 857: /***/ (module, exports, __webpack_require__) => {
+    /***/ 2774: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useIntersection", {
+        enumerable: true,
+        get: function () {
+          return useIntersection;
+        },
+      });
+      const _react = __webpack_require__(4312);
+      const _requestidlecallback = __webpack_require__(9413);
+      const hasIntersectionObserver =
+        typeof IntersectionObserver === "function";
+      const observers = new Map();
+      const idList = [];
+      function createObserver(options) {
+        const id = {
+          root: options.root || null,
+          margin: options.rootMargin || "",
+        };
+        const existing = idList.find(
+          (obj) => obj.root === id.root && obj.margin === id.margin
+        );
+        let instance;
+        if (existing) {
+          instance = observers.get(existing);
+          if (instance) {
+            return instance;
+          }
+        }
+        const elements = new Map();
+        const observer = new IntersectionObserver((entries) => {
+          entries.forEach((entry) => {
+            const callback = elements.get(entry.target);
+            const isVisible =
+              entry.isIntersecting || entry.intersectionRatio > 0;
+            if (callback && isVisible) {
+              callback(isVisible);
+            }
+          });
+        }, options);
+        instance = {
+          id,
+          observer,
+          elements,
+        };
+        idList.push(id);
+        observers.set(id, instance);
+        return instance;
+      }
+      function observe(element, callback, options) {
+        const { id, observer, elements } = createObserver(options);
+        elements.set(element, callback);
+        observer.observe(element);
+        return function unobserve() {
+          elements.delete(element);
+          observer.unobserve(element);
+          // Destroy observer when there's nothing left to watch:
+          if (elements.size === 0) {
+            observer.disconnect();
+            observers.delete(id);
+            const index = idList.findIndex(
+              (obj) => obj.root === id.root && obj.margin === id.margin
+            );
+            if (index > -1) {
+              idList.splice(index, 1);
+            }
+          }
+        };
+      }
+      function useIntersection({ rootRef, rootMargin, disabled }) {
+        const isDisabled = disabled || !hasIntersectionObserver;
+        const [visible, setVisible] = (0, _react.useState)(false);
+        const elementRef = (0, _react.useRef)(null);
+        const setElement = (0, _react.useCallback)((element) => {
+          elementRef.current = element;
+        }, []);
+        (0, _react.useEffect)(() => {
+          if (hasIntersectionObserver) {
+            if (isDisabled || visible) return;
+            const element = elementRef.current;
+            if (element && element.tagName) {
+              const unobserve = observe(
+                element,
+                (isVisible) => isVisible && setVisible(isVisible),
+                {
+                  root: rootRef?.current,
+                  rootMargin,
+                }
+              );
+              return unobserve;
+            }
+          } else {
+            if (!visible) {
+              const idleCallback = (0,
+              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
+              return () =>
+                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
+            }
+          }
+          // eslint-disable-next-line react-hooks/exhaustive-deps
+        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
+        const resetVisible = (0, _react.useCallback)(() => {
+          setVisible(false);
+        }, []);
+        return [setElement, visible, resetVisible];
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-intersection.js.map
+
+      /***/
+    },
+
+    /***/ 3351: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useMergedRef", {
+        enumerable: true,
+        get: function () {
+          return useMergedRef;
+        },
+      });
+      const _react = __webpack_require__(4312);
+      function useMergedRef(refA, refB) {
+        const cleanupA = (0, _react.useRef)(null);
+        const cleanupB = (0, _react.useRef)(null);
+        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
+        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
+        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
+        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
+        // (because it hasn't been updated for React 19)
+        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
+        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
+        return (0, _react.useCallback)(
+          (current) => {
+            if (current === null) {
+              const cleanupFnA = cleanupA.current;
+              if (cleanupFnA) {
+                cleanupA.current = null;
+                cleanupFnA();
+              }
+              const cleanupFnB = cleanupB.current;
+              if (cleanupFnB) {
+                cleanupB.current = null;
+                cleanupFnB();
+              }
+            } else {
+              if (refA) {
+                cleanupA.current = applyRef(refA, current);
+              }
+              if (refB) {
+                cleanupB.current = applyRef(refB, current);
+              }
+            }
+          },
+          [refA, refB]
+        );
+      }
+      function applyRef(refA, current) {
+        if (typeof refA === "function") {
+          const cleanup = refA(current);
+          if (typeof cleanup === "function") {
+            return cleanup;
+          } else {
+            return () => refA(null);
+          }
+        } else {
+          refA.current = current;
+          return () => {
+            refA.current = null;
+          };
+        }
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-merged-ref.js.map
+
+      /***/
+    },
+
+    /***/ 3440: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(4757);
+
+      /***/
+    },
+
+    /***/ 4757: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -23,22 +236,22 @@
           return useLinkStatus;
         },
       });
-      const _interop_require_wildcard = __webpack_require__(8781);
+      const _interop_require_wildcard = __webpack_require__(1162);
       const _jsxruntime = __webpack_require__(3108);
       const _react = /*#__PURE__*/ _interop_require_wildcard._(
         __webpack_require__(4312)
       );
-      const _resolvehref = __webpack_require__(4055);
-      const _islocalurl = __webpack_require__(7175);
-      const _formaturl = __webpack_require__(9674);
-      const _utils = __webpack_require__(7424);
-      const _addlocale = __webpack_require__(589);
-      const _routercontextsharedruntime = __webpack_require__(7010);
-      const _useintersection = __webpack_require__(2330);
-      const _getdomainlocale = __webpack_require__(7207);
-      const _addbasepath = __webpack_require__(9942);
-      const _usemergedref = __webpack_require__(8067);
-      const _erroronce = __webpack_require__(1945);
+      const _resolvehref = __webpack_require__(4691);
+      const _islocalurl = __webpack_require__(4763);
+      const _formaturl = __webpack_require__(3246);
+      const _utils = __webpack_require__(7372);
+      const _addlocale = __webpack_require__(1809);
+      const _routercontextsharedruntime = __webpack_require__(2254);
+      const _useintersection = __webpack_require__(2774);
+      const _getdomainlocale = __webpack_require__(8803);
+      const _addbasepath = __webpack_require__(5834);
+      const _usemergedref = __webpack_require__(3351);
+      const _erroronce = __webpack_require__(6021);
       const prefetched = new Set();
       function prefetch(router, href, as, options) {
         if (false) {
@@ -417,168 +630,43 @@
       /***/
     },
 
-    /***/ 1945: /***/ (__unused_webpack_module, exports) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "errorOnce", {
-        enumerable: true,
-        get: function () {
-          return errorOnce;
+    /***/ 5326: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/link",
+        function () {
+          return __webpack_require__(9058);
         },
-      });
-      let errorOnce = (_) => {};
+      ]);
       if (false) {
-      } //# sourceMappingURL=error-once.js.map
+      }
 
       /***/
     },
 
-    /***/ 2330: /***/ (module, exports, __webpack_require__) => {
+    /***/ 6021: /***/ (__unused_webpack_module, exports) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
         value: true,
       });
-      Object.defineProperty(exports, "useIntersection", {
+      Object.defineProperty(exports, "errorOnce", {
         enumerable: true,
         get: function () {
-          return useIntersection;
+          return errorOnce;
         },
       });
-      const _react = __webpack_require__(4312);
-      const _requestidlecallback = __webpack_require__(601);
-      const hasIntersectionObserver =
-        typeof IntersectionObserver === "function";
-      const observers = new Map();
-      const idList = [];
-      function createObserver(options) {
-        const id = {
-          root: options.root || null,
-          margin: options.rootMargin || "",
-        };
-        const existing = idList.find(
-          (obj) => obj.root === id.root && obj.margin === id.margin
-        );
-        let instance;
-        if (existing) {
-          instance = observers.get(existing);
-          if (instance) {
-            return instance;
-          }
-        }
-        const elements = new Map();
-        const observer = new IntersectionObserver((entries) => {
-          entries.forEach((entry) => {
-            const callback = elements.get(entry.target);
-            const isVisible =
-              entry.isIntersecting || entry.intersectionRatio > 0;
-            if (callback && isVisible) {
-              callback(isVisible);
-            }
-          });
-        }, options);
-        instance = {
-          id,
-          observer,
-          elements,
-        };
-        idList.push(id);
-        observers.set(id, instance);
-        return instance;
-      }
-      function observe(element, callback, options) {
-        const { id, observer, elements } = createObserver(options);
-        elements.set(element, callback);
-        observer.observe(element);
-        return function unobserve() {
-          elements.delete(element);
-          observer.unobserve(element);
-          // Destroy observer when there's nothing left to watch:
-          if (elements.size === 0) {
-            observer.disconnect();
-            observers.delete(id);
-            const index = idList.findIndex(
-              (obj) => obj.root === id.root && obj.margin === id.margin
-            );
-            if (index > -1) {
-              idList.splice(index, 1);
-            }
-          }
-        };
-      }
-      function useIntersection({ rootRef, rootMargin, disabled }) {
-        const isDisabled = disabled || !hasIntersectionObserver;
-        const [visible, setVisible] = (0, _react.useState)(false);
-        const elementRef = (0, _react.useRef)(null);
-        const setElement = (0, _react.useCallback)((element) => {
-          elementRef.current = element;
-        }, []);
-        (0, _react.useEffect)(() => {
-          if (hasIntersectionObserver) {
-            if (isDisabled || visible) return;
-            const element = elementRef.current;
-            if (element && element.tagName) {
-              const unobserve = observe(
-                element,
-                (isVisible) => isVisible && setVisible(isVisible),
-                {
-                  root: rootRef?.current,
-                  rootMargin,
-                }
-              );
-              return unobserve;
-            }
-          } else {
-            if (!visible) {
-              const idleCallback = (0,
-              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
-              return () =>
-                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
-            }
-          }
-          // eslint-disable-next-line react-hooks/exhaustive-deps
-        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
-        const resetVisible = (0, _react.useCallback)(() => {
-          setVisible(false);
-        }, []);
-        return [setElement, visible, resetVisible];
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-intersection.js.map
-
-      /***/
-    },
-
-    /***/ 4972: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/link",
-        function () {
-          return __webpack_require__(7600);
-        },
-      ]);
+      let errorOnce = (_) => {};
       if (false) {
-      }
+      } //# sourceMappingURL=error-once.js.map
 
       /***/
     },
 
-    /***/ 7207: /***/ (module, exports, __webpack_require__) => {
+    /***/ 8803: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -590,7 +678,7 @@
           return getDomainLocale;
         },
       });
-      const _normalizetrailingslash = __webpack_require__(151);
+      const _normalizetrailingslash = __webpack_require__(1315);
       const basePath =
         /* unused pure expression or super */ null && (false || "");
       function getDomainLocale(path, locale, locales, domainLocales) {
@@ -614,7 +702,7 @@
       /***/
     },
 
-    /***/ 7600: /***/ (
+    /***/ 9058: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -629,7 +717,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(3108);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7830);
+        __webpack_require__(3440);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_link__WEBPACK_IMPORTED_MODULE_1__
@@ -659,101 +747,13 @@
 
       /***/
     },
-
-    /***/ 7830: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(857);
-
-      /***/
-    },
-
-    /***/ 8067: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "useMergedRef", {
-        enumerable: true,
-        get: function () {
-          return useMergedRef;
-        },
-      });
-      const _react = __webpack_require__(4312);
-      function useMergedRef(refA, refB) {
-        const cleanupA = (0, _react.useRef)(null);
-        const cleanupB = (0, _react.useRef)(null);
-        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
-        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
-        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
-        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
-        // (because it hasn't been updated for React 19)
-        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
-        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
-        return (0, _react.useCallback)(
-          (current) => {
-            if (current === null) {
-              const cleanupFnA = cleanupA.current;
-              if (cleanupFnA) {
-                cleanupA.current = null;
-                cleanupFnA();
-              }
-              const cleanupFnB = cleanupB.current;
-              if (cleanupFnB) {
-                cleanupB.current = null;
-                cleanupFnB();
-              }
-            } else {
-              if (refA) {
-                cleanupA.current = applyRef(refA, current);
-              }
-              if (refB) {
-                cleanupB.current = applyRef(refB, current);
-              }
-            }
-          },
-          [refA, refB]
-        );
-      }
-      function applyRef(refA, current) {
-        if (typeof refA === "function") {
-          const cleanup = refA(current);
-          if (typeof cleanup === "function") {
-            return cleanup;
-          } else {
-            return () => refA(null);
-          }
-        } else {
-          refA.current = current;
-          return () => {
-            refA.current = null;
-          };
-        }
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-merged-ref.js.map
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(4972)
+      __webpack_exec__(5326)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for routerDirect-HASH.js
@@ -1,17 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [188],
   {
-    /***/ 1179: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9864);
-
-      /***/
-    },
-
-    /***/ 1640: /***/ (
+    /***/ 2014: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -26,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(3108);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(1179);
+        __webpack_require__(4169);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -46,7 +36,17 @@
       /***/
     },
 
-    /***/ 2172: /***/ (
+    /***/ 4169: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(9700);
+
+      /***/
+    },
+
+    /***/ 4414: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -54,7 +54,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/routerDirect",
         function () {
-          return __webpack_require__(1640);
+          return __webpack_require__(2014);
         },
       ]);
       if (false) {
@@ -68,7 +68,7 @@
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(2172)
+      __webpack_exec__(4414)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for script-HASH.js
@@ -1,17 +1,34 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [1209],
   {
-    /***/ 4977: /***/ (
+    /***/ 2591: /***/ (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
-      module.exports = __webpack_require__(326);
+      module.exports = __webpack_require__(4634);
 
       /***/
     },
 
-    /***/ 5887: /***/ (
+    /***/ 5030: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/script",
+        function () {
+          return __webpack_require__(7557);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 7557: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -26,7 +43,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(3108);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(4977);
+        __webpack_require__(2591);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_script__WEBPACK_IMPORTED_MODULE_1__
@@ -58,30 +75,13 @@
 
       /***/
     },
-
-    /***/ 6268: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/script",
-        function () {
-          return __webpack_require__(5887);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(6268)
+      __webpack_exec__(5030)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for withRouter-HASH.js
@@ -1,34 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [3263],
   {
-    /***/ 1179: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9864);
-
-      /***/
-    },
-
-    /***/ 2028: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/withRouter",
-        function () {
-          return __webpack_require__(4501);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 4501: /***/ (
+    /***/ 559: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -43,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(3108);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(1179);
+        __webpack_require__(4169);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -61,13 +34,40 @@
 
       /***/
     },
+
+    /***/ 726: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/withRouter",
+        function () {
+          return __webpack_require__(559);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 4169: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(9700);
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(2028)
+      __webpack_exec__(726)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for 2161-HASH.js

Diff too large to display

Diff for 2747-HASH.js

Diff too large to display

Diff for 4322-HASH.js
failed to diff
Diff for main-HASH.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js

Diff too large to display

Diff for app-page-tur..ntime.dev.js

Diff too large to display

Diff for app-page-tur..ntime.dev.js

Diff too large to display

Diff for app-page.runtime.dev.js

Diff too large to display

Diff for server.runtime.prod.js

Diff too large to display

Commit: f02153b

Comment thread packages/next/bin/next
Comment on lines +12 to +19
const binaryMap = {
'darwin-arm64': '@next/cli-darwin-arm64',
'darwin-x64': '@next/cli-darwin-x64',
'linux-arm64': '@next/cli-linux-arm64',
'linux-x64': '@next/cli-linux-x64',
'win32-x64': '@next/cli-win32-x64-msvc',
'win32-arm64': '@next/cli-win32-arm64-msvc',
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The platform-to-package mapping for Linux doesn't account for musl vs glibc variants, causing the native binary resolution to fail on Linux systems.

View Details
📝 Patch Details
diff --git a/packages/next/bin/next b/packages/next/bin/next
index 6bbc3b43ea..20458b829e 100755
--- a/packages/next/bin/next
+++ b/packages/next/bin/next
@@ -8,6 +8,40 @@ const { spawn } = require('child_process')
 const platform = process.platform
 const arch = process.arch
 
+/**
+ * Detect if the system is using musl libc instead of glibc.
+ * Returns true if musl, false if glibc.
+ */
+function isMusl() {
+  if (platform !== 'linux') {
+    return false
+  }
+  
+  try {
+    // If glibcVersionRuntime is present, it's glibc (not musl)
+    const report = process.report?.getReport()
+    if (report?.header?.glibcVersionRuntime) {
+      return false
+    }
+    
+    // Check for musl indicators in shared objects
+    if (Array.isArray(report?.sharedObjects)) {
+      for (const obj of report.sharedObjects) {
+        if (
+          typeof obj === 'string' &&
+          (obj.includes('libc.musl-') || obj.includes('ld-musl-'))
+        ) {
+          return true
+        }
+      }
+    }
+  } catch {
+    // Ignore errors, fall back to default
+  }
+  
+  return false
+}
+
 // Map to @next/cli-* package names
 const binaryMap = {
   'darwin-arm64': '@next/cli-darwin-arm64',
@@ -53,7 +87,13 @@ function runWithNode() {
 }
 
 const key = `${platform}-${arch}`
-const pkg = binaryMap[key]
+let pkg = binaryMap[key]
+
+// For Linux, append the libc variant (musl or gnu)
+if (pkg && platform === 'linux') {
+  const libc = isMusl() ? 'musl' : 'gnu'
+  pkg = `${pkg}-${libc}`
+}
 
 if (pkg) {
   try {

Analysis

Linux native binary resolution fails due to missing libc variant suffixes

What fails: packages/next/bin/next attempts to require @next/cli-linux-arm64 and @next/cli-linux-x64 packages that don't exist, causing require.resolve() to fail on line 62 and falling back to the Node.js implementation instead of using the native binary.

How to reproduce:

# On any Linux system (with glibc)
# The actual installed packages are:
# - @next/cli-linux-x64-gnu
# - @next/cli-linux-x64-musl
# But the code looks for:
# - @next/cli-linux-x64

node packages/next/bin/next
# This will fail to require.resolve() the package because the generic name doesn't exist

Result: The native binary is not used even when successfully installed. The code always falls back to running the Node.js implementation via runWithNode().

Expected: The code should detect the system's libc variant (musl or glibc) and append the appropriate suffix (-gnu or -musl) to construct the correct package name, matching the actual published packages:

  • @next/cli-linux-x64-gnu for glibc systems
  • @next/cli-linux-x64-musl for musl systems
  • @next/cli-linux-arm64-gnu for glibc systems
  • @next/cli-linux-arm64-musl for musl systems

References:

  • Actual package names defined in: crates/next-cli/npm/linux-*/package.json
  • Installation configured in: scripts/install-native.mjs
  • Reference implementation: rspack/crates/binding/index.js (uses process.report?.getReport().header.glibcVersionRuntime for detection)

feedthejim and others added 2 commits January 5, 2026 10:20
Replace fork-based architecture with single Node.js process:

- Add Rust CLI wrapper (`crates/next-cli/`) that handles:
  - NODE_OPTIONS (memory limits, source maps, inspect)
  - WATCHPACK_WATCHER_LIMIT for large projects
  - Restart loop (exit code 77) without Node.js overhead

- Simplify next-dev.ts: remove fork(), IPC, child process management
- Simplify start-server.ts: remove NEXT_PRIVATE_WORKER handling

- Add shell entry points (bin/next, bin/next.cmd) that:
  - Use native binary when available (~10ms overhead)
  - Fall back to Node.js entry point

- Add lazy loading for heavy modules:
  - tar (download-swc) - only needed for fallback download
  - edge-runtime sandbox - only needed for edge functions
  - MCP SDK - only needed when clients connect

- Set up cross-platform release pipeline:
  - Build @next/cli-* for 8 platforms in CI
  - Publish as optional dependencies
  - Update install-native.mjs for development

Saves ~35-50ms on dev server startup by eliminating parent
Node.js process and fork overhead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Yarn Berry PnP doesn't support shell script binaries - it tries to parse
them as JavaScript. Convert bin/next from shell script to JavaScript with
#!/usr/bin/env node shebang.

The JS wrapper:
- Detects platform/arch
- Looks for Rust CLI binary in @next/cli-* packages
- Falls back to dist/bin/next if not found

Benchmark: ~25-35ms overhead vs shell script (negligible for 500-1500ms startup)

Also removes next.cmd as npm/yarn auto-generate .cmd wrappers for JS bins.
@nextjs-bot

nextjs-bot commented Jan 5, 2026 •

Copy link
Copy Markdown
Contributor

❌ 119 failing tests in 27 jobs

Updated 2026-01-05 10:01:41 UTC · Commit: f02153b

File Failures Category
test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts 3 turbopack dev, cache components dev, dev
test/development/app-dir/server-navigation-error/server-navigation-error.test.ts 6 cache components dev, turbopack dev
test/development/app-dir/typed-env/typed-env-prod.test.ts 2 turbopack dev, dev
test/development/jsconfig-path-reloading/index.test.ts 5 dev
test/development/middleware-errors/index.test.ts 2 dev
test/development/tsconfig-path-reloading/index.test.ts 5 dev
test/e2e/app-dir/cache-components-errors/cache-components-errors.test.ts 6 turbopack production, prod
test/e2e/app-dir/disable-logging-route/disable-logging-route.test.ts 1 dev
test/e2e/app-dir/next-after-app/index.test.ts 2 turbopack production
test/e2e/app-dir/use-cache-unknown-cache-kind/use-cache-unknown-cache-kind.test.ts 2 cache components dev, turbopack dev
test/e2e/config-turbopack/index.test.ts 1 turbopack dev
...e-good-stack-traces-in-edge-runtime/fetch-failures-have-good-stack-traces-in-edge-runtime.test.ts 1 turbopack dev
test/e2e/next-script/index.test.ts 3 dev
test/integration/cli/test/index.test.ts 9 integration, turbopack production integration, turbopack development integration
test/integration/edge-runtime-dynamic-code/test/index.test.ts 6 turbopack development integration
test/integration/edge-runtime-with-node.js-apis/test/index.test.ts 38 integration
test/integration/server-side-dev-errors/test/index.test.ts 27 turbopack production integration, integration, turbopack development integration

Details

📁 test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts — 3 failures
app-dir - devtool-copy-button > should has inspect url copy button · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts -t "should has inspect url copy button"
page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('[data-nextjs-data-runtime-error-copy-devtools-url]') to be visible

    at waitForSelector (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:521:29)

    at Playwright._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Playwright._chain [as startChain] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:632:17)
    at Playwright.startChain [as waitForElementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:520:17)
    at Playwright.waitForElementByCss [as elementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:405:17)
    at Object.elementByCss (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:16:10)

    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:627:17)
    at Proxy.continueChain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:455:17)
    at Object.getAttribute (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:17:10)
app-dir - devtool-copy-button > should has inspect url copy button · cache components dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts -t "should has inspect url copy button"
page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('[data-nextjs-data-runtime-error-copy-devtools-url]') to be visible

    at waitForSelector (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:521:29)

    at Playwright._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Playwright._chain [as startChain] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:632:17)
    at Playwright.startChain [as waitForElementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:520:17)
    at Playwright.waitForElementByCss [as elementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:405:17)
    at Object.elementByCss (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:16:10)

    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:627:17)
    at Proxy.continueChain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:455:17)
    at Object.getAttribute (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:17:10)
app-dir - devtool-copy-button > should has inspect url copy button · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts -t "should has inspect url copy button"
page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('[data-nextjs-data-runtime-error-copy-devtools-url]') to be visible

    at waitForSelector (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:521:29)

    at Playwright._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Playwright._chain [as startChain] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:632:17)
    at Playwright.startChain [as waitForElementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:520:17)
    at Playwright.waitForElementByCss [as elementByCss] (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:405:17)
    at Object.elementByCss (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:16:10)

    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:651:23)
    at Proxy._chain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:627:17)
    at Proxy.continueChain (/root/actions-runner/_work/next.js/next.js/test/lib/browsers/playwright.ts:455:17)
    at Object.getAttribute (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/devtool-copy-button/devtool-copy-button.test.ts:17:10)
📁 test/development/app-dir/server-navigation-error/server-navigation-error.test.ts — 6 failures
server-navigation-error > pages router > should error on navigation API redirect · cache components dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API redirect"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error pages router should error on navigation API redirect 1`

- Snapshot  - 4
+ Received  + 2

  {
    "description": "Next.js navigation API is not allowed to be used in Pages Router.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "pages/pages/redirect.tsx (4:11) @ Page
- > 4 |   redirect('/')
-     |           ^",
+   "source": null,
    "stack": [
-     "Page pages/pages/redirect.tsx (4:11)",
+     "Page ./pages/redirect.tsx",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:12:29)
    at runNextTicks (node:internal/process/task_queues:60:5)
    at processImmediate (node:internal/timers:449:9)
    at process.callbackTrampoline (node:internal/async_hooks:130:17)
server-navigation-error > pages router > should error on navigation API notFound · cache components dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API notFound"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error pages router should error on navigation API notFound 1`

- Snapshot  - 4
+ Received  + 2

  {
    "description": "Next.js navigation API is not allowed to be used in Pages Router.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "pages/pages/not-found.tsx (4:11) @ Page
- > 4 |   notFound()
-     |           ^",
+   "source": null,
    "stack": [
-     "Page pages/pages/not-found.tsx (4:11)",
+     "Page ./pages/not-found.tsx",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:30:29)
    at runNextTicks (node:internal/process/task_queues:60:5)
    at processImmediate (node:internal/timers:449:9)
    at process.callbackTrampoline (node:internal/async_hooks:130:17)
server-navigation-error > pages router > should error on navigation API redirect · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API redirect"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error pages router should error on navigation API redirect 1`

- Snapshot  - 2
+ Received  + 2

  {
    "description": "Next.js navigation API is not allowed to be used in Pages Router.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "pages/pages/redirect.tsx (4:11) @ Page
+   "source": "pages/redirect.tsx (4:11) @ Page
  > 4 |   redirect('/')
      |           ^",
    "stack": [
-     "Page pages/pages/redirect.tsx (4:11)",
+     "Page pages/redirect.tsx (4:11)",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:12:29)
server-navigation-error > pages router > should error on navigation API notFound · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API notFound"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error pages router should error on navigation API notFound 1`

- Snapshot  - 2
+ Received  + 2

  {
    "description": "Next.js navigation API is not allowed to be used in Pages Router.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "pages/pages/not-found.tsx (4:11) @ Page
+   "source": "pages/not-found.tsx (4:11) @ Page
  > 4 |   notFound()
      |           ^",
    "stack": [
-     "Page pages/pages/not-found.tsx (4:11)",
+     "Page pages/not-found.tsx (4:11)",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:30:29)
server-navigation-error > middleware > should error on navigation API redirect · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API redirect "
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error middleware should error on navigation API redirect  1`

- Snapshot  - 4
+ Received  + 8

  {
    "description": "Next.js navigation API is not allowed to be used in Middleware.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "middleware.ts (8:13) @ middleware
- >  8 |     redirect('/')
-      |             ^",
+   "source": null,
    "stack": [
-     "middleware middleware.ts (8:13)",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:52:29)
    at runNextTicks (node:internal/process/task_queues:60:5)
    at processImmediate (node:internal/timers:449:9)
    at process.callbackTrampoline (node:internal/async_hooks:130:17)
server-navigation-error > middleware > should error on navigation API not-found · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/server-navigation-error/server-navigation-error.test.ts -t "should error on navigation API not-found"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `server-navigation-error middleware should error on navigation API not-found 1`

- Snapshot  - 4
+ Received  + 7

  {
    "description": "Next.js navigation API is not allowed to be used in Middleware.",
    "environmentLabel": null,
    "label": "Runtime Error",
-   "source": "middleware.ts (6:13) @ middleware
- > 6 |     notFound()
-     |             ^",
+   "source": null,
    "stack": [
-     "middleware middleware.ts (6:13)",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
+     "<FIXME-next-dist-dir>",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/server-navigation-error/server-navigation-error.test.ts:70:29)
📁 test/development/app-dir/typed-env/typed-env-prod.test.ts — 2 failures
typed-env > should have env types from next config · turbopack dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev-turbo test/development/app-dir/typed-env/typed-env-prod.test.ts -t "should have env types from next config"
Error: expect(received).not.toContain(expected) // indexOf

Expected substring: not "FROM_ENV_DEV"
Received string:        "// Type definitions for Next.js environment variables
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      /** Loaded from `.env.development.local` */
      FROM_ENV_DEV_LOCAL?: string
      /** Loaded from `.env.local` */
      FROM_ENV_LOCAL?: string
      /** Loaded from `.env.development` */
      FROM_ENV_DEV?: string
      /** Loaded from `.env` */
      FROM_ENV?: string
      /** Loaded from `next.config.js` */
      FROM_NEXT_CONFIG?: string
    }
  }
}
export {}"
    at toContain (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/typed-env/typed-env-prod.test.ts:17:26)
    at retry (/root/actions-runner/_work/next.js/next.js/test/lib/next-test-utils.ts:797:14)
    at Object.<anonymous> (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/typed-env/typed-env-prod.test.ts:13:5)
typed-env > should have env types from next config · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/app-dir/typed-env/typed-env-prod.test.ts -t "should have env types from next config"
Error: expect(received).not.toContain(expected) // indexOf

Expected substring: not "FROM_ENV_DEV"
Received string:        "// Type definitions for Next.js environment variables
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      /** Loaded from `.env.development.local` */
      FROM_ENV_DEV_LOCAL?: string
      /** Loaded from `.env.local` */
      FROM_ENV_LOCAL?: string
      /** Loaded from `.env.development` */
      FROM_ENV_DEV?: string
      /** Loaded from `.env` */
      FROM_ENV?: string
      /** Loaded from `next.config.js` */
      FROM_NEXT_CONFIG?: string
    }
  }
}
export {}"
    at toContain (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/typed-env/typed-env-prod.test.ts:17:26)
    at retry (/root/actions-runner/_work/next.js/next.js/test/lib/next-test-utils.ts:797:14)
    at Object.<anonymous> (/root/actions-runner/_work/next.js/next.js/test/development/app-dir/typed-env/typed-env-prod.test.ts:13:5)
📁 test/development/jsconfig-path-reloading/index.test.ts — 5 failures
jsconfig-path-reloading > jsconfig > should recover from module not found when paths is updated · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/jsconfig-path-reloading/index.test.ts -t "should recover from module not found when paths is updated"
Error: Expected no visible Redbox but found one
header: Build Error
Module not found: Can't resolve '@lib/second-data'
description: Module not found: Can't resolve '@lib/second-data'
source: ./pages/index.js (1:1)
Module not found: Can't resolve '@lib/second-data'
> 1 | import {secondData} from "@lib/second-data"
    | ^
  2 | import { Button1 } from '@c/button-1'
  3 | import { Button2 } from '@mybutton'
  4 | import { firstData } from '@lib/first-data'

https://nextjs.org/docs/messages/module-not-found
    at runNextTicks (node:internal/process/task_queues:60:5)
    at processImmediate (node:internal/timers:449:9)
    at process.callbackTrampoline (node:internal/async_hooks:130:17)
    at Object.<anonymous> (/root/actions-runner/_work/next.js/next.js/test/development/jsconfig-path-reloading/index.test.ts:106:9)
jsconfig-path-reloading > jsconfig > should automatically fast refresh content when path is added without error · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/jsconfig-path-reloading/index.test.ts -t "should automatically fast refresh content when path is added without error"
Error: Expected no visible Redbox but found one
header: Build Error
Module not found: Can't resolve '@myotherbutton'
description: Module not found: Can't resolve '@myotherbutton'
source: ./pages/index.js (2:1)
Module not found: Can't resolve '@myotherbutton'
  1 | import { Button1 } from '@c/button-1'
> 2 | import { Button2 } from '@myotherbutton'
    | ^
  3 | import { firstData } from '@lib/first-data'
  4 |
  5 | export default function Page(props) {

https://nextjs.org/docs/messages/module-not-found
    at Object.<anonymous> (/root/actions-runner/_work/next.js/next.js/test/development/jsconfig-path-reloading/index.test.ts:161:9)
jsconfig-path-reloading > jsconfig added after starting dev > should load with initial paths config correctly · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/jsconfig-path-reloading/index.test.ts -t "should load with initial paths config correctly"
Error: expect(received).toContain(expected) // indexOf

Expected substring: "first button"
Received string:    "<!DOCTYPE html><html><head><meta charSet=\"utf-8\" data-next-head=\"\"/><meta name=\"viewport\" content=\"width=device-width\" data-next-head=\"\"/><style data-next-hide-fouc=\"true\">body{display:none}</style><noscript data-next-hide-fouc=\"true\"><style>body{display:block}</style></noscript><noscript data-n-css=\"\"></noscript><script defer=\"\" noModule=\"\" src=\"/_next/static/chunks/polyfills.js\"></script><script src=\"/_next/static/chunks/fallback/webpack.js\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/main.js\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/pages/_app.js\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/pages/_error.js\" defer=\"\"></script><noscript id=\"__next_css__DO_NOT_USE__\"></noscript></head><body><div id=\"__next\"></div><script src=\"/_next/static/chunks/fallback/react-refresh.js\"></script><script id=\"__NEXT_DATA__\" type=\"application/json\">{\"props\":{\"pageProps\":{\"statusCode\":500,\"hostname\":\"localhost\"}},\"page\":\"/_error\",\"query\":{},\"buildId\":\"development\",\"isFallback\":false,\"err\":{\"name\":\"Error\",\"source\":\"server\",\"message\":\"Module not found: Can't resolve '@c/button-1'\\n\\u003e 1 | import { Button1 } from '@c/button-1'\\n    | ^\\n  2 | import { Button2 } from '@mybutton'\\n  3 | import { firstData } from '@lib/first-data'\\n  4 |\\n\\nhttps://nextjs.org/docs/messages/module-not-found\\n\",\"stack\":\"Error: Module not found: Can't resolve '@c/button-1'\\n\\u003e 1 | import { Button1 } from '@c/button-1'\\n    | ^\\n  2 | import { Button2 } from '@mybutton'\\n  3 | import { firstData } from '@lib/first-data'\\n  4 |\\n\\nhttps://nextjs.org/docs/messages/module-not-found\\n\\n    at getNotFoundError (/tmp/next-install-a7f43267a2fa270318342fc4ccf020a78dc2988db84150c6ece5b1321385f2de/node_modules/.pnpm/file+..+next-repo-54aa964e33ac
... (truncated)

See CI Logs or Datadog for full error

jsconfig-path-reloading > jsconfig added after starting dev > should recover from module not found when paths is updated · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/jsconfig-path-reloading/index.test.ts -t "should recover from module not found when paths is updated"
Error: TIMED OUT: success

<head><meta charset="utf-8" data-next-head=""><meta name="viewport" content="width=device-width" data-next-head=""><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js"></script><script src="/_next/static/chunks/fallback/webpack.js" defer=""></script><script src="/_next/static/chunks/fallback/main.js" defer=""></script><script src="/_next/static/chunks/fallback/pages/_app.js" defer=""></script><script src="/_next/static/chunks/fallback/pages/_error.js" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript><style>@font-face{font-family:'__nextjs-Geist';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'__nextjs-Geist Mono';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-mono-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'__nextjs-Geist';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'__nextjs-Geist Mono';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-mono-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style></head><body style
... (truncated)

See CI Logs or Datadog for full error

jsconfig-path-reloading > jsconfig added after starting dev > should automatically fast refresh content when path is added without error · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/jsconfig-path-reloading/index.test.ts -t "should automatically fast refresh content when path is added without error"
Error: TIMED OUT: success

<head><meta charset="utf-8" data-next-head=""><meta name="viewport" content="width=device-width" data-next-head=""><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js"></script><script src="/_next/static/chunks/fallback/webpack.js" defer=""></script><script src="/_next/static/chunks/fallback/main.js" defer=""></script><script src="/_next/static/chunks/fallback/pages/_app.js" defer=""></script><script src="/_next/static/chunks/fallback/pages/_error.js" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript><style>@font-face{font-family:'__nextjs-Geist';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'__nextjs-Geist Mono';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-mono-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'__nextjs-Geist';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'__nextjs-Geist Mono';font-style:normal;font-weight:400 600;font-display:swap;src:url(/__nextjs_font/geist-mono-latin.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}</style></head><body style
... (truncated)

See CI Logs or Datadog for full error

📁 test/development/middleware-errors/index.test.ts — 2 failures
middleware - development errors > when throwing while loading the module > logs the error correctly · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/middleware-errors/index.test.ts -t "logs the error correctly"
Error: expect(received).toContain(expected) // indexOf

Expected substring: "
⨯ Error: booooom!
    at <unknown> (middleware.js:3)
    at eval (middleware.js:3:13)
    at (middleware)/./middleware.js (.next/dev/server/middleware.js:18:1)
    at __webpack_require__ "
Received string:    "▲ Next.js 16.1.1-canary.12 (webpack)
- Local:         http://localhost:46277
- Network:       http://65.21.85.156:46277
- Experiments (use with caution):
  ✓ strictRouteTypes (enabled by `__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES`)·
⚠ The \"middleware\" file convention is deprecated. Please use \"proxy\" instead. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy
✓ Ready in 900ms
⨯ Error: booooom!
    at <unknown> (middleware.js:3)
    at eval (middleware.js:3:13)
    at <unknown> (middleware)/./middleware.js (.next/dev/server/middleware.js:18:1)
    at __webpack_require__ (.next/dev/server/edge-runtime-webpack.js:37:33)
    at fn (.next/dev/server/edge-runtime-webpack.js:273:21)
    at __webpack_require__ (.next/dev/server/edge-runtime-webpack.js:37:33)
    at __webpack_exec__ (.next/dev/server/middleware.js:1043:48)
    at <unknown> (.next/dev/server/middleware.js:1044:37)
    at webpackJsonpCallback (.next/dev/server/edge-runtime-webpack.js:1128:39)
  1 |
  2 |       import { NextResponse } from 'next/server'
> 3 |       throw new Error('booooom!')
    |             ^
  4 |       export default function () {
  5 |         return NextResponse.next()
  6 |       }
"
    at Object.toContain (/root/actions-runner/_work/next.js/next.js/test/development/middleware-errors/index.test.ts:304:41)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
middleware - development errors > when throwing while loading the module > renders the error correctly and recovers · dev

Datadog Job · 7-day History · CI Logs

pnpm test-dev test/development/middleware-errors/index.test.ts -t "renders the error correctly and recovers"
Error: expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `middleware - development errors when throwing while loading the module renders the error correctly and recovers 1`

- Snapshot  - 1
+ Received  + 0

@@ -13,8 +13,7 @@
      "<FIXME-next-dist-dir>",
      "<FIXME-next-dist-dir>",
      "<FIXME-next-dist-dir>",
      "<FIXME-next-dist-dir>",
      "<FIXME-next-dist-dir>",
-     "<FIXME-next-dist-dir>",
    ],
  }
    at Object.toDisplayRedbox (/root/actions-runner/_work/next.js/next.js/test/development/middleware-errors/index.test.ts:366:31)

⚠️ 101 more failures (details omitted due to size limit)

...r/cache-components-errors/cache-components-errors.test.ts (6)

Cache Components Errors > Build With --prerender-debug > Inside use cache > throwing an error at runtime > should log an error at runtime

  • turbopack production · DD
  • prod · DD

Cache Components Errors > Build With --prerender-debug > Inside use cache > catching an error at runtime > should log an error at runtime

  • turbopack production · DD
  • prod · DD

Cache Components Errors > Build Without --prerender-debug > Inside use cache > throwing an error at runtime > should log an error at runtime

  • turbopack production · DD

Cache Components Errors > Build Without --prerender-debug > Inside use cache > catching an error at runtime > should log an error at runtime

  • turbopack production · DD
...p-dir/disable-logging-route/disable-logging-route.test.ts (1)

app-dir - disable-logging-route > should not log if disabled logging

  • dev · DD
test/e2e/app-dir/next-after-app/index.test.ts (2)

after() in nodejs runtime > does not allow modifying cookies in a callback

  • turbopack production · DD

after() in edge runtime > does not allow modifying cookies in a callback

  • turbopack production · DD
...e-unknown-cache-kind/use-cache-unknown-cache-kind.test.ts (2)

use-cache-unknown-cache-kind > should recover from the build error if the cache handler is defined

  • cache components dev · DD
  • turbopack dev · DD
test/e2e/config-turbopack/index.test.ts (1)

config-turbopack > when turbopack is auto selected > when webpack is configured but Turbopack is not > warns

  • turbopack dev · DD
...h-failures-have-good-stack-traces-in-edge-runtime.test.ts (1)

fetch failures have good stack traces in edge runtime > when awaiting fetch using an unknown domain, stack traces are preserved

  • turbopack dev · DD
test/e2e/next-script/index.test.ts (3)

experimental.nextScriptWorkers > experimental.nextScriptWorkers: true with required Partytown dependency for external script > Worker scripts are modified by Partytown to execute on a worker thread

  • dev · DD

experimental.nextScriptWorkers > experimental.nextScriptWorkers: true with required Partytown dependency for inline script > Inline worker script through children is modified by Partytown to execute on a worker thread

  • dev · DD

experimental.nextScriptWorkers > experimental.nextScriptWorkers: true with required Partytown dependency for inline script > Inline worker script through dangerouslySetInnerHtml is modified by Partytown to execute on a worker thread

  • dev · DD
test/integration/cli/test/index.test.ts (9)

CLI Usage > dev > --inspect

  • integration · DD
  • turbopack production integration · DD
  • turbopack development integration · DD

CLI Usage > dev > --inspect 0

  • integration · DD
  • turbopack production integration · DD
  • turbopack development integration · DD

CLI Usage > dev > --inspect [port]

  • integration · DD
  • turbopack production integration · DD
  • turbopack development integration · DD
.../integration/edge-runtime-dynamic-code/test/index.test.ts (6)

Middleware usage of dynamic code evaluation > development mode > shows a warning when running code with eval

  • turbopack development integration · DD

Middleware usage of dynamic code evaluation > development mode > shows a warning when running WebAssembly.compile

  • turbopack development integration · DD

Middleware usage of dynamic code evaluation > development mode > shows a warning when running WebAssembly.instantiate with a buffer parameter

  • turbopack development integration · DD

Edge route usage of dynamic code evaluation > development mode > shows a warning when running code with eval

  • turbopack development integration · DD

Edge route usage of dynamic code evaluation > development mode > shows a warning when running WebAssembly.compile

  • turbopack development integration · DD

Edge route usage of dynamic code evaluation > development mode > shows a warning when running WebAssembly.instantiate with a buffer parameter

  • turbopack development integration · DD
...gration/edge-runtime-with-node.js-apis/test/index.test.ts (38)

Middleware using Node.js API > development mode > throws error when using setImmediate

  • integration · DD

Middleware using Node.js API > development mode > throws error when using clearImmediate

  • integration · DD

Middleware using Node.js API > development mode > throws error when using process.cwd

  • integration · DD

Middleware using Node.js API > development mode > throws error when using process.cpuUsage

  • integration · DD

Middleware using Node.js API > development mode > throws error when using process.getuid

  • integration · DD

Middleware using Node.js API > development mode > throws error when using BroadcastChannel

  • integration · DD

Middleware using Node.js API > development mode > throws error when using ByteLengthQueuingStrategy

  • integration · DD

Middleware using Node.js API > development mode > throws error when using CompressionStream

  • integration · DD

Middleware using Node.js API > development mode > throws error when using CountQueuingStrategy

  • integration · DD

Middleware using Node.js API > development mode > throws error when using DecompressionStream

  • integration · DD

Middleware using Node.js API > development mode > throws error when using DomException

  • integration · DD

Middleware using Node.js API > development mode > throws error when using MessageChannel

  • integration · DD

Middleware using Node.js API > development mode > throws error when using MessageEvent

  • integration · DD

Middleware using Node.js API > development mode > throws error when using MessagePort

  • integration · DD

Middleware using Node.js API > development mode > throws error when using ReadableByteStreamController

  • integration · DD

Middleware using Node.js API > development mode > throws error when using ReadableStreamBYOBRequest

  • integration · DD

Middleware using Node.js API > development mode > throws error when using ReadableStreamDefaultController

  • integration · DD

Middleware using Node.js API > development mode > throws error when using TransformStreamDefaultController

  • integration · DD

Middleware using Node.js API > development mode > throws error when using WritableStreamDefaultController

  • integration · DD

Edge route using Node.js API > development mode > throws error when using setImmediate

  • integration · DD

Edge route using Node.js API > development mode > throws error when using clearImmediate

  • integration · DD

Edge route using Node.js API > development mode > throws error when using process.cwd

  • integration · DD

Edge route using Node.js API > development mode > throws error when using process.cpuUsage

  • integration · DD

Edge route using Node.js API > development mode > throws error when using process.getuid

  • integration · DD

Edge route using Node.js API > development mode > throws error when using BroadcastChannel

  • integration · DD

Edge route using Node.js API > development mode > throws error when using ByteLengthQueuingStrategy

  • integration · DD

Edge route using Node.js API > development mode > throws error when using CompressionStream

  • integration · DD

Edge route using Node.js API > development mode > throws error when using CountQueuingStrategy

  • integration · DD

Edge route using Node.js API > development mode > throws error when using DecompressionStream

  • integration · DD

Edge route using Node.js API > development mode > throws error when using DomException

  • integration · DD

Edge route using Node.js API > development mode > throws error when using MessageChannel

  • integration · DD

Edge route using Node.js API > development mode > throws error when using MessageEvent

  • integration · DD

Edge route using Node.js API > development mode > throws error when using MessagePort

  • integration · DD

Edge route using Node.js API > development mode > throws error when using ReadableByteStreamController

  • integration · DD

Edge route using Node.js API > development mode > throws error when using ReadableStreamBYOBRequest

  • integration · DD

Edge route using Node.js API > development mode > throws error when using ReadableStreamDefaultController

  • integration · DD

Edge route using Node.js API > development mode > throws error when using TransformStreamDefaultController

  • integration · DD

Edge route using Node.js API > development mode > throws error when using WritableStreamDefaultController

  • integration · DD
test/integration/server-side-dev-errors/test/index.test.ts (27)

server-side dev errors > should show server-side error for gsp page correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for gssp page correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for dynamic gssp page correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for api route correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for dynamic api route correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for uncaught rejection correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for uncaught empty rejection correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for uncaught exception correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

server-side dev errors > should show server-side error for uncaught empty exception correctly

  • turbopack production integration · DD
  • integration · DD
  • turbopack development integration · DD

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

CI Bypass Graphite Optimization Ignore Graphite CI optimizations, run the full CI suite. https://graphite.dev/docs/stacking-and-ci created-by: Next.js team PRs by the Next.js team. locked tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants