Repository: vercel/next.js
Pull request: Webpack build: Add compiled in x seconds in missing places

### What?

Added duration of the webpack build in a few missing places. This was already added earlier but it seems we missed 2 cases where it logs `compiled successfully` but did not include a timing. E.g. shadcn-ui's apps/v4 did not show the webpack compile time.

### How?
- Moved the `durationToString` function from `build/index.ts` to a dedicated file
- Added duration information to successful compilation messages in webpack build
- Updated log messages to show compilation time in appropriate units (ms, s, min)

Fixes #

Closes PACK-4268

Changed files:
filename: packages/next/src/build/duration-to-string.ts
patch: @@ -0,0 +1,13 @@
+export function durationToString(compilerDuration: number) {
+  let durationString
+  if (compilerDuration > 120) {
+    durationString = `${(compilerDuration / 60).toFixed(1)}min`
+  } else if (compilerDuration > 40) {
+    durationString = `${compilerDuration.toFixed(0)}s`
+  } else if (compilerDuration > 2) {
+    durationString = `${compilerDuration.toFixed(1)}s`
+  } else {
+    durationString = `${(compilerDuration * 1000).toFixed(0)}ms`
+  }
+  return durationString
+}

filename: packages/next/src/build/index.ts
patch: @@ -209,6 +209,7 @@ import { turbopackBuild } from './turbopack-build'
 import { isPersistentCachingEnabled } from '../shared/lib/turbopack/utils'
 import { inlineStaticEnv } from '../lib/inline-static-env'
 import { populateStaticEnv } from '../lib/static-env'
+import { durationToString } from './duration-to-string'
 
 type Fallback = null | boolean | string
 
@@ -3725,17 +3726,3 @@ export default async function build(
     }
   }
 }
-
-function durationToString(compilerDuration: number) {
-  let durationString
-  if (compilerDuration > 120) {
-    durationString = `${(compilerDuration / 60).toFixed(1)}min`
-  } else if (compilerDuration > 40) {
-    durationString = `${compilerDuration.toFixed(0)}s`
-  } else if (compilerDuration > 2) {
-    durationString = `${compilerDuration.toFixed(1)}s`
-  } else {
-    durationString = `${(compilerDuration * 1000).toFixed(0)}ms`
-  }
-  return durationString
-}

filename: packages/next/src/build/webpack-build/impl.ts
patch: @@ -40,6 +40,7 @@ import type { UnwrapPromise } from '../../lib/coalesced-function'
 
 import origDebug from 'next/dist/compiled/debug'
 import { Telemetry } from '../../telemetry/storage'
+import { durationToString } from '../duration-to-string'
 
 const debug = origDebug('next:build:webpack-build')
 
@@ -340,12 +341,15 @@ export async function webpackBuildImpl(
     err.code = 'WEBPACK_ERRORS'
     throw err
   } else {
+    const duration = webpackBuildEnd[0]
+    const durationString = durationToString(duration)
+
     if (result.warnings.length > 0) {
-      Log.warn('Compiled with warnings\n')
+      Log.warn(`Compiled with warnings in ${durationString}\n`)
       console.warn(result.warnings.filter(Boolean).join('\n\n'))
       console.warn()
     } else if (!compilerName) {
-      Log.event('Compiled successfully')
+      Log.event(`Compiled successfully in ${durationString}`)
     }
 
     return {

filename: packages/next/src/build/webpack-build/index.ts
patch: @@ -11,6 +11,7 @@ import {
   getParsedNodeOptionsWithoutInspect,
 } from '../../server/lib/utils'
 import { mergeUseCacheTrackers } from '../webpack/plugins/telemetry-plugin/use-cache-tracker-utils'
+import { durationToString } from '../duration-to-string'
 
 const debug = origDebug('next:build:webpack-build')
 
@@ -119,7 +120,8 @@ async function webpackBuildWithWorker(
   }
 
   if (compilerNames.length === 3) {
-    Log.event('Compiled successfully')
+    const durationString = durationToString(combinedResult.duration)
+    Log.event(`Compiled successfully in ${durationString}`)
   }
 
   return combinedResult