[npm] Produce more readable build error messages.

The `npm run build` and `npm start` were producing quite messy error
messages, which are hard to parse, and the `esbuild` and `tsc` error
message format was also (naturally) different. This change adjusts
the error messages to always report in the `tsc` format (which is
well understood and supported by IDEs and tools). It also removes
the unnecessary clutter from the error messages and simplifies it to
basically this (for `tsc`)

```
✖ TypeScript compilation failed for `Debug'

front_end/panels/timeline/components/insights/Checklist.ts(46,1): error TS1128: Declaration or statement expected.
front_end/panels/timeline/components/insights/Checklist.ts(46,8): error TS1434: Unexpected keyword or identifier.
front_end/panels/timeline/components/insights/Checklist.ts(46,8): error TS2304: Cannot find name 'TableDataRow'.
front_end/panels/timeline/components/insights/Checklist.ts(48,3): error TS2552: Cannot find name 'overlays'. Did you mean 'Overlays'?
front_end/panels/timeline/components/insights/Checklist.ts(48,12): error TS1109: Expression expected.
front_end/panels/timeline/components/insights/Checklist.ts(48,14): error TS1361: 'Overlays' cannot be used as a value because it was imported using 'import type'.
front_end/panels/timeline/components/insights/Checklist.ts(48,32): error TS2339: Property 'TimelineOverlay' does not exist on type 'typeof import("/usr/local/google/home/bmeurer/Projects/devtools/devtools-frontend/out/Debug/gen/front_end/panels/timeline/overlays/OverlaysImpl")'.
front_end/panels/timeline/components/insights/Checklist.ts(48,48): error TS1011: An element access expression should take an argument.
front_end/panels/timeline/components/insights/Table.ts(26,71): error TS1005: ',' expected.
front_end/panels/timeline/components/insights/Table.ts(26,71): error TS2304: Cannot find name 'a'.
front_end/panels/timeline/components/insights/Table.ts(26,74): error TS2345: Argument of type 'RegisteredFileStrings' is not assignable to parameter of type 'string'.
front_end/panels/timeline/components/insights/Table.ts(66,39): error TS2554: Expected 0-1 arguments, but got 2.
```

and this (for `esbuild`):

```
✖ TypeScript compilation failed for `Default'

front_end/panels/timeline/components/insights/Checklist.ts(46,7): error TS0666: Unexpected "TableDataRow"
front_end/panels/timeline/components/insights/Table.ts(26,70): error TS0666: Expected ")" but found "a"
```

Bug: 411328609
Change-Id: Ic0be7780a91810386e38997c7237210aa69f63dd
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6469693
Reviewed-by: Nikolay Vitkov <[email protected]>
Auto-Submit: Benedikt Meurer <[email protected]>
Commit-Queue: Benedikt Meurer <[email protected]>
diff --git a/scripts/devtools_build.mjs b/scripts/devtools_build.mjs
index 36bdbcc..9062fa3 100644
--- a/scripts/devtools_build.mjs
+++ b/scripts/devtools_build.mjs
@@ -127,8 +127,56 @@
   }
 }
 
+/**
+ * Constructs a human readable error message for the given build `error`.
+ *
+ * @param {Error} error the `Error` from the failed `autoninja` invocation.
+ * @param {string} outDir the absolute path to the `target` out directory.
+ * @param {string} target the targe relative to `//out`.
+ * @return {string} the human readable error message.
+ */
+function buildErrorMessageForNinja(error, outDir, target) {
+  const {message, stderr, stdout} = error;
+  if (stderr) {
+    // Anything that went to stderr has precedence.
+    return `Failed to build \`${target}' in \`${outDir}'
+
+${stderr}
+`;
+  }
+  if (stdout) {
+    // Check for `tsc` or `esbuild` errors in the stdout.
+    const tscErrors = [...stdout.matchAll(/^[^\s].*\(\d+,\d+\): error TS\d+:\s+.*$/gm)].map(([tscError]) => tscError);
+    if (!tscErrors.length) {
+      // We didn't find any `tsc` errors, but maybe there are `esbuild` errors.
+      // Transform these into the `tsc` format (with a made up error code), so
+      // we can report all TypeScript errors consistently in `tsc` format (which
+      // is well-known and understood by tools).
+      const esbuildErrors = stdout.matchAll(/^✘ \[ERROR\] ([^\n]+)\n\n\s+\.\.\/\.\.\/(.+):(\d+):(\d+):/gm);
+      for (const [, message, filename, line, column] of esbuildErrors) {
+        tscErrors.push(`${filename}(${line},${column}): error TS0000: ${message}`);
+      }
+    }
+    if (tscErrors.length) {
+      return `TypeScript compilation failed for \`${target}'
+
+${tscErrors.join('\n')}
+`;
+    }
+
+    // At the very least we strip `ninja: Something, something` lines from the
+    // standard output, since that's not particularly helpful.
+    const output = stdout.replaceAll(/^ninja: [^\n]+\n+/mg, '').trim();
+    return `Failed to build \`${target}' in \`${outDir}'
+
+${output}
+`;
+  }
+  return `Failed to build \`${target}' in \`${outDir}' (${message.substring(0, message.indexOf('\n'))})`;
+}
+
 export const BuildStep = {
-  GN: 'gn-gen',
+  GN: 'gn',
   AUTONINJA: 'autoninja',
 };
 
@@ -144,22 +192,14 @@
    */
   constructor(step, options) {
     const {cause, outDir, target} = options;
-    super(`Failed to build target ${target} in ${outDir}`, {cause});
-    this.name = 'BuildError';
+    const message = step === BuildStep.GN ? `\`gn' failed to initialize out directory ${outDir}` :
+                                            buildErrorMessageForNinja(cause, outDir, target);
+    super(message, {cause});
     this.step = step;
+    this.name = 'BuildError';
     this.target = target;
     this.outDir = outDir;
   }
-
-  toString() {
-    const {cause} = this;
-    const {stdout} = cause;
-    if (stdout) {
-      return stdout;
-    }
-    const {message} = this;
-    return `${message}\n${cause.message}`;
-  }
 }
 
 /**