Skip to content

Commit

Permalink
fix(core): show better errors
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed Jan 8, 2025
1 parent 8dc100a commit 9ed7de2
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 48 deletions.
51 changes: 36 additions & 15 deletions packages/nx/src/project-graph/error-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@ import { ProjectGraph } from '../config/project-graph';
import { CreateNodesFunctionV2 } from './plugins/public-api';

export class ProjectGraphError extends Error {
readonly #errors: Array<
| AggregateCreateNodesError
| MergeNodesError
| CreateMetadataError
| ProjectsWithNoNameError
| MultipleProjectsWithSameNameError
| ProcessDependenciesError
| WorkspaceValidityError
>;
readonly #partialProjectGraph: ProjectGraph;
readonly #partialSourceMaps: ConfigurationSourceMaps;

constructor(
errors: Array<
private readonly errors: Array<
| AggregateCreateNodesError
| MergeNodesError
| ProjectsWithNoNameError
Expand All @@ -33,15 +24,15 @@ export class ProjectGraphError extends Error {
partialSourceMaps: ConfigurationSourceMaps
) {
super(
`Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it.`
`Failed to process project graph, there may be an issue with one of your Nx plugins. If the cause is not obvious from the error message, running "nx reset" may fix it. Please report the issue if you keep seeing it.`
);
this.name = this.constructor.name;
this.#errors = errors;
this.errors = errors;
this.#partialProjectGraph = partialProjectGraph;
this.#partialSourceMaps = partialSourceMaps;
this.stack = `${this.message}\n ${errors
this.stack = errors
.map((error) => indentString(formatErrorStackAndCause(error), 2))
.join('\n')}`;
.join('\n');
}

/**
Expand All @@ -67,7 +58,7 @@ export class ProjectGraphError extends Error {
}

getErrors() {
return this.#errors;
return this.errors;
}
}

Expand Down Expand Up @@ -242,6 +233,36 @@ export class AggregateCreateNodesError extends Error {
}
}

export function formatAggregateCreateNodesError(
error: AggregateCreateNodesError,
pluginName: string
) {
error.message = `${
error.errors.length > 1 ? `${error.errors.length} errors` : 'An error'
} occurred while processing files for the ${pluginName} plugin.`;

const errorBodyLines = [];
const errorStackLines = [];

const innerErrors = error.errors;
for (const [file, e] of innerErrors) {
if (file) {
errorBodyLines.push(` - ${file}: ${e.message}`);
errorStackLines.push(` - ${file}: ${e.stack}`);
} else {
errorBodyLines.push(` - ${e.message}`);
errorStackLines.push(` - ${e.stack}`);
}
if (e.stack && process.env.NX_VERBOSE_LOGGING === 'true') {
const innerStackTrace = ' ' + e.stack.split('\n')?.join('\n ');
errorStackLines.push(innerStackTrace);
}
}

error.stack = errorStackLines.join('\n');
error.message = errorBodyLines.join('\n');
}

export class MergeNodesError extends Error {
file: string;
pluginName: string;
Expand Down
22 changes: 2 additions & 20 deletions packages/nx/src/project-graph/utils/project-configuration-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
isProjectWithNoNameError,
isAggregateCreateNodesError,
AggregateCreateNodesError,
formatAggregateCreateNodesError,
} from '../error-types';
import { CreateNodesResult } from '../plugins/public-api';
import { isGlobPattern } from '../../utils/globs';
Expand Down Expand Up @@ -392,31 +393,12 @@ export async function createProjectConfigurations(
workspaceRoot: root,
})
.catch((e: Error) => {
const errorBodyLines = [
`An error occurred while processing files for the ${pluginName} plugin.`,
];
const error: AggregateCreateNodesError = isAggregateCreateNodesError(e)
? // This is an expected error if something goes wrong while processing files.
e
: // This represents a single plugin erroring out with a hard error.
new AggregateCreateNodesError([[null, e]], []);

const innerErrors = error.errors;
for (const [file, e] of innerErrors) {
if (file) {
errorBodyLines.push(` - ${file}: ${e.message}`);
} else {
errorBodyLines.push(` - ${e.message}`);
}
if (e.stack) {
const innerStackTrace =
' ' + e.stack.split('\n')?.join('\n ');
errorBodyLines.push(innerStackTrace);
}
}

error.stack = errorBodyLines.join('\n');

formatAggregateCreateNodesError(error, pluginName);
// This represents a single plugin erroring out with a hard error.
errors.push(error);
// The plugin didn't return partial results, so we return an empty array.
Expand Down
7 changes: 5 additions & 2 deletions packages/nx/src/utils/handle-errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ describe('handleErrors', () => {
const body = bodyLines.join('\n');
expect(body).toContain('cause message');
expect(body).toContain('test-plugin');
// --verbose is active, so we should see the stack trace
expect(body).toMatch(/\s+at.*handle-errors.spec.ts/);
});

it('should only display wrapper error if not verbose', async () => {
it('should not display stack trace if not verbose', async () => {
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
await handleErrors(false, async () => {
const cause = new Error('cause message');
Expand All @@ -41,7 +43,8 @@ describe('handleErrors', () => {

const { bodyLines, title } = spy.mock.calls[0][0];
const body = bodyLines.join('\n');
expect(body).not.toContain('cause message');
expect(body).toContain('cause message');
expect(body).not.toMatch(/\s+at.*handle-errors.spec.ts/);
});

it('should display misc errors that do not have a cause', async () => {
Expand Down
22 changes: 11 additions & 11 deletions packages/nx/src/utils/handle-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,20 @@ export async function handleErrors(
) {
title += ' ' + projectGraphError.cause.message + '.';
}
if (isVerbose) {
title += ' See errors below.';
}

const bodyLines = isVerbose
? formatErrorStackAndCause(projectGraphError)
: ['Pass --verbose to see the stacktraces.'];
title += ' See errors below.';

output.error({
title,
bodyLines: bodyLines,
bodyLines: isVerbose
? formatErrorStackAndCause(projectGraphError, isVerbose)
: projectGraphError.getErrors().map((e) => e.message),
});
} else {
const lines = (err.message ? err.message : err.toString()).split('\n');
const bodyLines: string[] = lines.slice(1);
if (isVerbose) {
bodyLines.push(...formatErrorStackAndCause(err));
bodyLines.push(...formatErrorStackAndCause(err, isVerbose));
} else if (err.stack) {
bodyLines.push('Pass --verbose to see the stacktrace.');
}
Expand All @@ -59,13 +56,16 @@ export async function handleErrors(
}
}

function formatErrorStackAndCause<T extends Error>(error: T): string[] {
function formatErrorStackAndCause<T extends Error>(
error: T,
verbose: boolean
): string[] {
return [
error.stack || error.message,
verbose ? error.stack || error.message : error.message,
...(error.cause && typeof error.cause === 'object'
? [
'Caused by:',
'stack' in error.cause
verbose && 'stack' in error.cause
? error.cause.stack.toString()
: error.cause.toString(),
]
Expand Down

0 comments on commit 9ed7de2

Please sign in to comment.