vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.79k stars 26.95k forks source link

Page Router - Turbopack HMR Warning: window.next.router undefined during appIsrManifest event #71974

Open gurkerl83 opened 1 week ago

gurkerl83 commented 1 week ago

Link to the code that reproduces this issue

https://github.com/gurkerl83/next-turbo-dynamic-import

To Reproduce

The following reproducer was create for another ticket. The HMR problem is observable even in this demonstrator, so I hope this is OK for reuse.

  1. Clone the reproduction repository:
git clone https://github.com/gurkerl83/next-turbo-dynamic-import.git
  1. The reproduction case was originally created to address a different issue. To use it for this problem, please comment out both the import and render of the ComponentNamedImportWithIssue component.

  2. Install dependencies using pnpm:

pnpm install
  1. Run the development server with Turbopack:
pnpm dev:turbo
  1. Go to the browser, Open Dev Tools, Observe the console warnings and errors upon startup:
[HMR] Invalid message: {"action":"appIsrManifest","data":{}}
TypeError: Cannot read properties of undefined (reading 'pathname')
    at http://localhost:3000/_next/static/chunks/...

Current vs. Expected behavior

Current behavior: When starting the application with Turbopack in development mode, a TypeError occurs due to window.next.router being undefined during HMR events, specifically during the appIsrManifest action. This results in console warnings and may disrupt the development workflow.

Expected behavior: The application should start without any TypeError or HMR warnings. Hot Module Replacement (HMR) events should not cause errors even if window.next.router is not yet initialized.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.5.0: Wed May  1 20:17:33 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6031
  Available memory (MB): 65536
  Available CPU cores: 16
Binaries:
  Node: 22.6.0
  npm: 10.8.2
  Yarn: N/A
  pnpm: 9.11.0
Relevant Packages:
  next: 15.0.2-canary.9 // Latest available version.
  react: 19.0.0-rc-cae764ce-20241025
  react-dom: 19.0.0-rc-cae764ce-20241025
  typescript: 5.6.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Pages Router, Turbopack

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

In the Next.js source code, there are two distinct hot-reloader-client implementations: one for the Page Router and another for the App Router.

The Page Router variant includes a custom event handler, customHmrEventHandler, which is responsible for handling specific hot-reload events. This functionality is implemented in the following code snippet to address the issue effectively.

packages/next/src/client/dev/hot-middleware-client.ts

Note: This issue may not be detectable in the App Router because the event handler in question is not utilized there. The reproduction case specifically relies on the Page Router.

import type {
  NextRouter,
  PrivateRouteInfo,
} from '../../shared/lib/router/router'
import connect from '../components/react-dev-overlay/pages/hot-reloader-client'
import { sendMessage } from '../components/react-dev-overlay/pages/websocket'

// Define a local type for the window.next object
interface NextWindow {
  next?: {
    router?: NextRouter & {
      components: { [pathname: string]: PrivateRouteInfo }
    }
  }
  __nextDevClientId?: string
  location: Location
}

declare const window: NextWindow

let reloading = false

export default (mode: 'webpack' | 'turbopack') => {
  const devClient = connect(mode)

  devClient.subscribeToHmrEvent((obj) => {
    if (reloading) return

    // Retrieve the router if it's available
    const router = window.next?.router

    // Determine if we're on an error page or the router is not initialized
    const isOnErrorPage =
      !router || router.pathname === '/404' || router.pathname === '/_error'

    switch (obj.action) {
      case 'reloadPage': {
        sendMessage(
          JSON.stringify({
            event: 'client-reload-page',
            clientId: window.__nextDevClientId,
          })
        )
        reloading = true
        return window.location.reload()
      }
      case 'removedPage': {
        const [page] = obj.data

        // Check if the removed page is the current page
        const isCurrentPage = page === router?.pathname

        if (isCurrentPage || isOnErrorPage) {
          sendMessage(
            JSON.stringify({
              event: 'client-removed-page',
              clientId: window.__nextDevClientId,
              page,
            })
          )
          return window.location.reload()
        }
        return
      }
      case 'addedPage': {
        const [page] = obj.data

        // Check if the added page is the current page
        const isCurrentPage = page === router?.pathname

        // Check if the page component is not yet loaded
        const isPageNotLoaded =
          typeof router?.components?.[page] === 'undefined'

        if ((isCurrentPage && isPageNotLoaded) || isOnErrorPage) {
          sendMessage(
            JSON.stringify({
              event: 'client-added-page',
              clientId: window.__nextDevClientId,
              page,
            })
          )
          return window.location.reload()
        }
        return
      }
      case 'serverError':
      case 'devPagesManifestUpdate':
      case 'appIsrManifest':
      case 'building':
      case 'finishBuilding': {
        return
      }
      default: {
        throw new Error('Unexpected action ' + obj.action)
      }
    }
  })

  return devClient
}
0xfabricio commented 6 days ago

⬆️ same problem

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!