Repository: vercel/next.js
File: packages/next/src/server/lib/start-server.ts (TypeScript)

Code before:
                  | undefined
                if (telemetry) {
                  // Use flushDetached to avoid blocking process exit
                  // Each process writes to a unique file (_events_${pid}.json)
                  // to avoid race conditions with the parent process
                  telemetry.flushDetached('dev', dir)
                }
              } catch (_) {
                // Ignore telemetry errors during cleanup
              }
            }

            debug('start-server process cleanup finished')
            process.exit(0)
          })()
        }

        // Make sure commands gracefully respect termination signals (e.g. from Docker)
        // Allow the graceful termination to be manually configurable
        if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
          process.on('SIGINT', cleanup)
          process.on('SIGTERM', cleanup)
        }

        // Load config and acquire lock BEFORE printing URLs
        // This ensures we fail early if another dev server is running
        const initResult = await getRequestHandlers({
          dir,
          port,
          isDev,
          onDevServerCleanup: cleanupListeners
            ? cleanupListeners.add.bind(cleanupListeners)
            : undefined,
          server,
          hostname,
          minimalMode,
          keepAliveTimeout,
          experimentalHttpsServer: !!selfSignedCertificate,
        })
        requestHandler = initResult.requestHandler
        upgradeHandler = initResult.upgradeHandler
        nextServer = initResult.server
        closeUpgraded = initResult.closeUpgraded

        // Only print URLs after lock acquisition succeeds
        const envInfo = isDev ? getEnvInfo(dir) : undefined
        logStartInfo({
          networkUrl,
          appUrl,
          envInfo,
          logBundler: isDev,

Review comment (bug):
We want to print the URL as soon as possible, so that the user can click on it and open their browser. The user's action of opening the link in their browser will likely take a second or two, so there's no good reason we can't do other initialization work in parallel.

The URL is ready as soon as we have the port open, if the user opens their browser very quickly and makes a request a little early, it just blocks until we've finished loading the config and starting the server.

This is important in making the CLI feel as responsive as possible.

This does mean that sometimes we'll print the URL and then fail to acquire the lock (which depends on the config), but it's better to optimize for the happy path than to make the error output marginally better.

Code after:
              if (isDev) {
                server.closeAllConnections()
                closeUpgraded?.()
              }
            })

            // now that no new requests can come in, clean up the rest
            await Promise.all([
              nextServer?.close().catch(console.error),
              cleanupListeners?.runAll().catch(console.error),
            ])

            // Flush telemetry if this is a dev server
            if (isDev) {
              try {
                const { traceGlobals } =
                  require('../../trace/shared') as typeof import('../../trace/shared')
                const telemetry = traceGlobals.get('telemetry') as
                  | InstanceType<
                      typeof import('../../telemetry/storage').Telemetry
                    >
                  | undefined
                if (telemetry) {
                  // Use flushDetached to avoid blocking process exit
                  // Each process writes to a unique file (_events_${pid}.json)
                  // to avoid race conditions with the parent process
                  telemetry.flushDetached('dev', dir)
                }
              } catch (_) {
                // Ignore telemetry errors during cleanup
              }
            }

            debug('start-server process cleanup finished')
            process.exit(0)
          })()
        }

        // Make sure commands gracefully respect termination signals (e.g. from Docker)
        // Allow the graceful termination to be manually configurable
        if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
          process.on('SIGINT', cleanup)
          process.on('SIGTERM', cleanup)
        }

        // Now load config via getRequestHandlers (single loadConfig call)
        const initResult = await getRequestHandlers({
          dir,
          port,
          isDev,
          onDevServerCleanup: cleanupListeners