libs/nx/src/migrations/utils/manual-review.ts
A place the migration deliberately left untouched because there is no safe mechanical rewrite. Surfaced to the user at the end of the run so they can fix it by hand.
Properties |
| file | |
| Type |
string
|
|
Description
|
Workspace-relative path of the file. |
| line | |
| Type |
number
|
|
Description
|
1-based line number within that file. |
| reason | |
| Type |
string
|
|
Description
|
Why it could not be migrated automatically. |
| snippet | |
| Type |
string
|
|
Description
|
The offending source snippet, trimmed to a single line. |
import { logger } from '@nx/devkit';
/**
* A place the migration deliberately left untouched because there is no safe
* mechanical rewrite. Surfaced to the user at the end of the run so they can fix it
* by hand.
*/
export interface ManualReviewItem {
/** Workspace-relative path of the file. */
file: string;
/** 1-based line number within that file. */
line: number;
/** The offending source snippet, trimmed to a single line. */
snippet: string;
/** Why it could not be migrated automatically. */
reason: string;
}
/**
* Prints the collected manual-review items, grouped by file, via the Nx logger.
* No-op when there is nothing to report. `migrationLabel` identifies the calling
* migration in the header (e.g. "Building Block `id`", "signal-queries").
*/
export function reportManualReviewItems(items: ManualReviewItem[], migrationLabel: string): void {
if (items.length === 0) return;
// De-duplicate items that resolve to the same file+line+reason.
const seen = new Set<string>();
const byFile = new Map<string, ManualReviewItem[]>();
for (const item of items) {
const key = `${item.file}:${item.line}:${item.reason}`;
if (seen.has(key)) continue;
seen.add(key);
const bucket = byFile.get(item.file) ?? [];
bucket.push(item);
byFile.set(item.file, bucket);
}
const lines: string[] = [
'',
`The ${migrationLabel} migration left ${seen.size} location(s) unchanged because`,
'they cannot be migrated automatically. Please review and update them manually:',
''
];
for (const [file, fileItems] of byFile) {
lines.push(` ${file}`);
for (const item of fileItems.sort((a, b) => a.line - b.line)) {
lines.push(` L${item.line} ${item.snippet}`);
lines.push(` ↳ ${item.reason}`);
}
lines.push('');
}
logger.warn(lines.join('\n'));
}
/** Collapses a multi-line snippet to a single trimmed line for display. */
export function toSnippet(text: string): string {
return text.replace(/\s+/g, ' ').trim();
}