Skip to content

Commit

Permalink
feat(devkit): add aggregateLog util for executor to plugin migration (n…
Browse files Browse the repository at this point in the history
…rwl#26523)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

## Current Behavior
<!-- This is the behavior we have today -->
There is no way to aggregate logs and output at the end of execution for
the executor to plugin migrations

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
There should be a way to aggregate logs and output them at the end of
execution

Usage:
```ts
// Call with the executorName that is being migrated
// Make sure the log itself does not contain dynamic values
// Add the project root that is being migrated

aggregateLog({executorName: '@nx/vite:build', project: projectRoot, log: 'Encountered X in project.json. Do Y to handle this manually.'})

// Then in GeneratorCallback run a task
return () => { flushLogs() };
```

Example Output

![image](https://github.com/nrwl/nx/assets/12140467/9a36c855-7377-4ad5-946c-c50202d9b50c)


## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
  • Loading branch information
Coly010 authored Jun 13, 2024
1 parent 92c94d8 commit 364198f
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { logger } from 'nx/src/devkit-exports';
import { AggregatedLog } from './aggregate-log-util';

describe(`aggregateLog utils`, () => {
it('should aggregate similar logs to single log listing the affected projects', () => {
// ARRANGE
let spyLog = '';
jest.spyOn(logger, 'warn').mockImplementation((log) => (spyLog = log));
const aggregatedLogs = new AggregatedLog();
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'app',
});
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'myapp',
});
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'shop-app',
});

// ACT
aggregatedLogs.flushLogs();

// ASSERT
expect(logger.warn).toHaveBeenCalled();
expect(spyLog).toMatchInlineSnapshot(`
"Encountered the following while migrating '@nx/vite:serve':
 • Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.
Affected Projects
app
myapp
shop-app
"
`);
});

it('should aggregate similar logs to single log and output different logs correctly', () => {
// ARRANGE
let spyLog = '';
jest.spyOn(logger, 'warn').mockImplementation((log) => (spyLog = log));
const aggregatedLogs = new AggregatedLog();
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'app',
});
aggregatedLogs.addLog({
executorName: '@nx/vite:build',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'myapp',
});
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
project: 'shop-app',
});
aggregatedLogs.addLog({
executorName: '@nx/vite:serve',
log: `Encountered 'AnotherValue' in project.json. You will need to copy the contents of this file to the 'config.prop' property in your Vite config file.`,
project: 'shop-app',
});

// ACT
aggregatedLogs.flushLogs();

// ASSERT
expect(logger.warn).toHaveBeenCalled();
expect(spyLog).toMatchInlineSnapshot(`
"Encountered the following while migrating '@nx/vite:serve':
 • Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.
Affected Projects
app
shop-app
• Encountered 'AnotherValue' in project.json. You will need to copy the contents of this file to the 'config.prop' property in your Vite config file.
Affected Projects
shop-app
Encountered the following while migrating '@nx/vite:build':
 • Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.
Affected Projects
myapp
"
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { output, logger } from 'nx/src/devkit-exports';

interface AggregateLogOptions {
project: string;
log: string;
executorName: string;
}

interface AggregateLogItem {
log: string;
projects: Set<string>;
}

/**
* @example
* // Instantiate a new object
* const migrationLogs = new AggregatedLog();
*
* // Add logs
* migrationLogs.addLog({executorName: '@nx/vite:build', project: 'app1', log: 'Migrate X manually'});
*
* // Flush all logs
* migrationLogs.flushLogs()
*/
export class AggregatedLog {
logs: Map<string, Map<string, AggregateLogItem>> = new Map();

addLog({ project, log, executorName }: AggregateLogOptions): void {
if (!this.logs.has(executorName)) {
this.logs.set(executorName, new Map());
}

const executorLogs = this.logs.get(executorName);
if (!executorLogs.has(log)) {
executorLogs.set(log, { log, projects: new Set([project]) });
} else {
const logItem = executorLogs.get(log);
logItem.projects.add(project);
}
}

reset(): void {
this.logs.clear();
}

flushLogs(): void {
let fullLog = '';
for (const executorName of this.logs.keys()) {
fullLog = `${fullLog}${output.bold(
`Encountered the following while migrating '${executorName}':\r\n`
)}`;
for (const logItem of this.logs.get(executorName).values()) {
fullLog = `${fullLog}${logItem.log}\r\n`;
fullLog = `${fullLog} ${output.bold(`Affected Projects`)}\r\n`;
fullLog = `${fullLog} ${Array.from(logItem.projects.values()).join(
`\r\n `
)}`;
fullLog = `${fullLog}\r\n`;
}
}

logger.warn(fullLog);

this.reset();
}
}

0 comments on commit 364198f

Please sign in to comment.