| 166 | } |
| 167 | |
| 168 | export function formatHandleErrors( |
| 169 | errors: Array<Error>, |
| 170 | config: Config.ProjectConfig, |
| 171 | ): Array<string> { |
| 172 | const stacks = new Map<string, {stack: string; names: Set<string>}>(); |
| 173 | |
| 174 | for (const err of errors) { |
| 175 | const formatted = formatExecError( |
| 176 | err, |
| 177 | config, |
| 178 | {noStackTrace: false}, |
| 179 | undefined, |
| 180 | true, |
| 181 | ); |
| 182 | |
| 183 | // E.g. timeouts might give multiple traces to the same line of code |
| 184 | // This hairy filtering tries to remove entries with duplicate stack traces |
| 185 | |
| 186 | const ansiFree: string = stripAnsi(formatted); |
| 187 | const match = ansiFree.match(/\s+at(.*)/); |
| 188 | if (!match || match.length < 2) { |
| 189 | continue; |
| 190 | } |
| 191 | |
| 192 | const stackText = ansiFree.slice(ansiFree.indexOf(match[1])).trim(); |
| 193 | |
| 194 | const name = ansiFree.match(/(?<=● {2}).*$/m); |
| 195 | if (name == null || name.length === 0) { |
| 196 | continue; |
| 197 | } |
| 198 | |
| 199 | const stack = stacks.get(stackText) || { |
| 200 | names: new Set(), |
| 201 | stack: formatted.replace(name[0], '%%OBJECT_NAME%%'), |
| 202 | }; |
| 203 | |
| 204 | stack.names.add(name[0]); |
| 205 | |
| 206 | stacks.set(stackText, stack); |
| 207 | } |
| 208 | |
| 209 | return [...stacks.values()].map(({stack, names}) => |
| 210 | stack.replace('%%OBJECT_NAME%%', [...names].join(',')), |
| 211 | ); |
| 212 | } |