(errors: ValidationError[])
| 355 | * Formats validation errors for display |
| 356 | */ |
| 357 | export function formatValidationErrors(errors: ValidationError[]): string { |
| 358 | let errorsByPackageDirName: Record<string, ValidationError[]> = {}; |
| 359 | for (let error of errors) { |
| 360 | if (!errorsByPackageDirName[error.packageDirName]) { |
| 361 | errorsByPackageDirName[error.packageDirName] = []; |
| 362 | } |
| 363 | errorsByPackageDirName[error.packageDirName].push(error); |
| 364 | } |
| 365 | |
| 366 | let lines: string[] = []; |
| 367 | |
| 368 | for (let [packageDirName, packageErrors] of Object.entries( |
| 369 | errorsByPackageDirName, |
| 370 | )) { |
| 371 | lines.push(`📦 ${packageDirName}:`); |
| 372 | for (let error of packageErrors) { |
| 373 | lines.push(` ${error.file}: ${error.error}`); |
| 374 | } |
| 375 | lines.push(""); |
| 376 | } |
| 377 | |
| 378 | let packageCount = Object.keys(errorsByPackageDirName).length; |
| 379 | lines.push( |
| 380 | `Found ${errors.length} error${errors.length === 1 ? "" : "s"} in ${packageCount} package${packageCount === 1 ? "" : "s"}`, |
| 381 | ); |
| 382 | |
| 383 | return lines.join("\n"); |
| 384 | } |
| 385 | |
| 386 | /** |
| 387 | * Checks if content starts with "BREAKING CHANGE: " (case-insensitive, |
no test coverage detected
searching dependent graphs…