Repository: vercel/next.js
Pull request: Build: Highlight TypeScript and ESLint being run clearer

## What?

Follow-up to #84602.

- Renamed `Linting and checking validity of types` to `Running ESLint and TypeScript concurrently`
- When `Running ESLint and TypeScript concurrently` finishes it logs the individual times for `TypeScript` and `ESLint` separately

Previous PR only showed the time for TypeScript, hiding the time it took to run ESlint.

The reason for renaming it to `Running ESLint and TypeScript` is that it does not hide what is happening under the hood. Someone might assume Next.js is doing extra work here, but it is calling the underlying tools in the application to check linting and types. We've seen cases where running TypeScript here is 1 minute or more, that would previously be hidden or hard to investigate for the developer as they didn't know what "linting and type checking" entails in this context.

```
   Creating an optimized production build ...
 ✓ Compiled successfully in 764ms
 ✓ Finished TypeScript in 968ms    
 ✓ Finished ESLint in 824ms    
 ✓ Collecting page data in 203ms    
 ✓ Generating static pages (4/4) in 318ms
 ✓ Collecting build traces in 3.3s    
 ✓ Finalizing page optimization in 3.3s    
```

Closes PACK-5640

Changed files:
filename: packages/next/src/build/type-check.ts
patch: @@ -110,11 +110,11 @@ export async function startTypeChecking({
 
   if (!ignoreTypeScriptErrors && shouldLint) {
     typeCheckingAndLintingSpinnerPrefixText =
-      'Linting and checking validity of types'
+      'Running ESLint and TypeScript concurrently'
   } else if (!ignoreTypeScriptErrors) {
-    typeCheckingAndLintingSpinnerPrefixText = 'Checking validity of types'
+    typeCheckingAndLintingSpinnerPrefixText = 'Running TypeScript'
   } else if (shouldLint) {
-    typeCheckingAndLintingSpinnerPrefixText = 'Linting'
+    typeCheckingAndLintingSpinnerPrefixText = 'Running ESLint'
   }
 
   // we will not create a spinner if both ignoreTypeScriptErrors and ignoreESLint are
@@ -125,10 +125,10 @@ export async function startTypeChecking({
     )
   }
 
-  const typeCheckStart = process.hrtime()
+  const typeCheckAndLintStart = process.hrtime()
 
   try {
-    const [[verifyResult, typeCheckEnd]] = await Promise.all([
+    const [[verifyResult, typeCheckEnd], lintCheckEnd] = await Promise.all([
       nextBuildSpan.traceChild('run-typescript').traceAsyncFn(() =>
         verifyTypeScriptSetup(
           dir,
@@ -142,7 +142,7 @@ export async function startTypeChecking({
           !!appDir,
           !!pagesDir
         ).then((resolved) => {
-          const checkEnd = process.hrtime(typeCheckStart)
+          const checkEnd = process.hrtime(typeCheckAndLintStart)
           return [resolved, checkEnd] as const
         })
       ),
@@ -155,14 +155,22 @@ export async function startTypeChecking({
             config.experimental.workerThreads,
             telemetry
           )
+          const checkEnd = process.hrtime(typeCheckAndLintStart)
+          return checkEnd
         }),
     ])
 
     if (typeCheckingAndLintingSpinner) {
-      typeCheckingAndLintingSpinner.setText(
-        `${typeCheckingAndLintingSpinnerPrefixText} in ${hrtimeDurationToString(typeCheckEnd)}`
-      )
-      typeCheckingAndLintingSpinner.stopAndPersist()
+      typeCheckingAndLintingSpinner.stop()
+
+      createSpinner(
+        `Finished TypeScript${ignoreTypeScriptErrors ? ' config validation' : ''} in ${hrtimeDurationToString(typeCheckEnd)}`
+      )?.stopAndPersist()
+      if (lintCheckEnd) {
+        createSpinner(
+          `Finished ESLint in ${hrtimeDurationToString(lintCheckEnd)}`
+        )?.stopAndPersist()
+      }
     }
 
     if (!ignoreTypeScriptErrors && verifyResult) {

filename: test/production/app-dir/build-output/index.test.ts
patch: @@ -24,7 +24,7 @@ describe('production - app dir - build output', () => {
       'Creating an optimized production build'
     )
     const indexOfLinting = output.indexOf(
-      'Linting and checking validity of types'
+      'Running ESLint and TypeScript concurrently'
     )
     expect(indexOfVersion).toBeLessThan(indexOfLinting)
     expect(indexOfStartCompiling).toBeLessThan(indexOfLinting)