Skip to content

Commit

Permalink
fix(core): handleErrors should display error cause if it exists (#27886)
Browse files Browse the repository at this point in the history
Some error messages are not displaying properly, as they pass their
original message as a cause. While `node` supports this, our
`handleErrors` function was not displaying error causes.

```
"Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it.
            CreateMetadataError: The "test-plugin" plugin threw an error while creating metadata: cause message
            at /Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:17:29
            at handleErrors (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.ts:11:26)
            at Object.<anonymous> (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:15:23)
            at Promise.then.completed (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
            at new Promise (<anonymous>)
            at callAsyncCircusFn (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
            at _callCircusTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
            at async _runTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
            at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
            at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
            at async run (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
            at async runAndTransformResultsToJestFormat (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
            at async jestAdapter (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
            at async runTestInternal (/Users/agentender/repos/nx/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
            at async runTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)
        Caused by:
            Error: cause message
              at /Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:16:21
              at handleErrors (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.ts:11:26)
              at Object.<anonymous> (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:15:23)
              at Promise.then.completed (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:298:28)
              at new Promise (<anonymous>)
              at callAsyncCircusFn (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/utils.js:231:10)
              at _callCircusTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:316:40)
              at async _runTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:252:3)
              at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:126:9)
              at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:121:9)
              at async run (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/run.js:71:3)
              at async runAndTransformResultsToJestFormat (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
              at async jestAdapter (/Users/agentender/repos/nx/node_modules/.pnpm/jest-circus@29.7.0/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
              at async runTestInternal (/Users/agentender/repos/nx/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:367:16)
              at async runTest (/Users/agentender/repos/nx/node_modules/.pnpm/jest-runner@29.7.0/node_modules/jest-runner/build/runTest.js:444:34)"
    `
```

(cherry picked from commit 1924bc3)
  • Loading branch information
AgentEnder authored and FrozenPandaz committed Sep 12, 2024
1 parent 42cab26 commit 068b066
Show file tree
Hide file tree
Showing 26 changed files with 203 additions and 84 deletions.
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/add/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { writeJsonFile } from '../../utils/fileutils';
import { logger } from '../../utils/logger';
import { output } from '../../utils/output';
import { getPackageManagerCommand } from '../../utils/package-manager';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { getPluginCapabilities } from '../../utils/plugins';
import { nxVersion } from '../../utils/versions';
import { workspaceRoot } from '../../utils/workspace-root';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/affected/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
withRunOptions,
withTargetAndConfigurationOption,
} from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export const yargsAffectedCommand: CommandModule = {
command: 'affected',
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/deprecated/command-objects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommandModule } from 'yargs';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import {
withAffectedOptions,
withTargetAndConfigurationOption,
Expand Down
3 changes: 1 addition & 2 deletions packages/nx/src/command-line/generate/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import {
import { logger, NX_PREFIX } from '../../utils/logger';
import {
combineOptionsForGenerator,
handleErrors,
Options,
Schema,
} from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { getLocalWorkspacePlugins } from '../../utils/plugins/local-plugins';
import { printHelp } from '../../utils/print-help';
import { workspaceRoot } from '../../utils/workspace-root';
import { NxJsonConfiguration } from '../../config/nx-json';
import { calculateDefaultProjectName } from '../../config/calculate-default-project-name';
import { findInstalledPlugins } from '../../utils/plugins/installed-plugins';
import { getGeneratorInformation } from './generator-utils';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/import/command-object.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CommandModule } from 'yargs';
import { linkToNxDevAndExamples } from '../yargs-utils/documentation';
import { withVerbose } from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export const yargsImportCommand: CommandModule = {
command: 'import [sourceRepository] [destinationDirectory]',
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/login/login.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { verifyOrUpdateNxCloudClient } from '../../nx-cloud/update-manager';
import { getCloudOptions } from '../../nx-cloud/utilities/get-cloud-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export interface LoginArgs {
nxCloudUrl?: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/logout/logout.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { verifyOrUpdateNxCloudClient } from '../../nx-cloud/update-manager';
import { getCloudOptions } from '../../nx-cloud/utilities/get-cloud-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export interface LogoutArgs {
verbose?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/migrate/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
packageRegistryView,
resolvePackageVersionUsingRegistry,
} from '../../utils/package-manager';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import {
connectToNxCloudWithPrompt,
onlyDefaultRunnerIsUsed,
Expand Down
3 changes: 2 additions & 1 deletion packages/nx/src/command-line/new/new.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { flushChanges, FsTree } from '../../generators/tree';
import { combineOptionsForGenerator, handleErrors } from '../../utils/params';
import { combineOptionsForGenerator } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { getGeneratorInformation } from '../generate/generator-utils';

function removeSpecialFlags(generatorOptions: { [p: string]: any }): void {
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { createProjectGraphAsync } from '../../project-graph/project-graph';
import { interpolate } from '../../tasks-runner/utils';
import { isCI } from '../../utils/is-ci';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { joinPathFragments } from '../../utils/path';
import { workspaceRoot } from '../../utils/workspace-root';
import { ChangelogOptions } from './command-object';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/plan-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
splitArgsIntoNxArgsAndOverrides,
} from '../../utils/command-line-utils';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { PlanCheckOptions, PlanOptions } from './command-object';
import {
createNxReleaseConfig,
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
splitArgsIntoNxArgsAndOverrides,
} from '../../utils/command-line-utils';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { PlanOptions } from './command-object';
import {
createNxReleaseConfig,
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
readGraphFileFromGraphArg,
} from '../../utils/command-line-utils';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { projectHasTarget } from '../../utils/project-graph-utils';
import { generateGraph } from '../graph/graph';
import { PublishOptions } from './command-object';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/release/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { NxReleaseConfiguration, readNxJson } from '../../config/nx-json';
import { createProjectFileMapUsingProjectGraph } from '../../project-graph/file-map-utils';
import { createProjectGraphAsync } from '../../project-graph/project-graph';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import {
createAPI as createReleaseChangelogAPI,
shouldCreateGitHubRelease,
Expand Down
3 changes: 2 additions & 1 deletion packages/nx/src/command-line/release/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
readProjectsConfigurationFromProjectGraph,
} from '../../project-graph/project-graph';
import { output } from '../../utils/output';
import { combineOptionsForGenerator, handleErrors } from '../../utils/params';
import { combineOptionsForGenerator } from '../../utils/params';
import { joinPathFragments } from '../../utils/path';
import { workspaceRoot } from '../../utils/workspace-root';
import { parseGeneratorString } from '../generate/generate';
Expand Down Expand Up @@ -52,6 +52,7 @@ import {
createGitTagValues,
handleDuplicateGitTags,
} from './utils/shared';
import { handleErrors } from '../../utils/handle-errors';

const LARGE_BUFFER = 1024 * 1000000;

Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/repair/repair.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import * as migrationsJson from '../../../migrations.json';
import { executeMigrations } from '../migrate/migrate';
import { output } from '../../utils/output';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/run-many/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
withOverrides,
withBatch,
} from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export const yargsRunManyCommand: CommandModule = {
command: 'run-many',
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/run/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
withOverrides,
withRunOneOptions,
} from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export const yargsRunCommand: CommandModule = {
command: 'run [project][:target][:configuration] [_..]',
Expand Down
7 changes: 2 additions & 5 deletions packages/nx/src/command-line/run/run.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { env as appendLocalEnv } from 'npm-run-path';
import {
combineOptionsForExecutor,
handleErrors,
Schema,
} from '../../utils/params';
import { combineOptionsForExecutor, Schema } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import { printHelp } from '../../utils/print-help';
import { NxJsonConfiguration } from '../../config/nx-json';
import { relative } from 'path';
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/show/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
withAffectedOptions,
withVerbose,
} from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';

export interface NxShowArgs {
json?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/src/command-line/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as ora from 'ora';
import { readNxJson } from '../../config/nx-json';
import { createProjectGraphAsync } from '../../project-graph/project-graph';
import { output } from '../../utils/output';
import { handleErrors } from '../../utils/params';
import { handleErrors } from '../../utils/handle-errors';
import {
collectAllRegisteredSyncGenerators,
flushSyncGeneratorChanges,
Expand Down
41 changes: 34 additions & 7 deletions packages/nx/src/project-graph/error-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class ProjectGraphError extends Error {
this.#partialProjectGraph = partialProjectGraph;
this.#partialSourceMaps = partialSourceMaps;
this.stack = `${this.message}\n ${errors
.map((error) => error.stack.split('\n').join('\n '))
.map((error) => indentString(formatErrorStackAndCause(error), 2))
.join('\n')}`;
}

Expand Down Expand Up @@ -263,23 +263,29 @@ export class MergeNodesError extends Error {
this.name = this.constructor.name;
this.file = file;
this.pluginName = pluginName;
this.stack = `${this.message}\n ${error.stack.split('\n').join('\n ')}`;
this.stack = `${this.message}\n${indentString(
formatErrorStackAndCause(error),
2
)}`;
}
}

export class CreateMetadataError extends Error {
constructor(public readonly error: Error, public readonly plugin: string) {
super(`The "${plugin}" plugin threw an error while creating metadata:`, {
cause: error,
});
super(
`The "${plugin}" plugin threw an error while creating metadata: ${error.message}`,
{
cause: error,
}
);
this.name = this.constructor.name;
}
}

export class ProcessDependenciesError extends Error {
constructor(public readonly pluginName: string, { cause }) {
super(
`The "${pluginName}" plugin threw an error while creating dependencies:`,
`The "${pluginName}" plugin threw an error while creating dependencies: ${cause.message}`,
{
cause,
}
Expand Down Expand Up @@ -316,7 +322,7 @@ export class ProcessProjectGraphError extends Error {
}
);
this.name = this.constructor.name;
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
this.stack = `${this.message}\n${indentString(cause, 2)}`;
}
}

Expand Down Expand Up @@ -394,3 +400,24 @@ export class LoadPluginError extends Error {
this.name = this.constructor.name;
}
}

function indentString(str: string, indent: number): string {
return (
' '.repeat(indent) +
str
.split('\n')
.map((line) => ' '.repeat(indent) + line)
.join('\n')
);
}

function formatErrorStackAndCause(error: Error): string {
const cause =
error.cause && error.cause instanceof Error ? error.cause : null;
return (
error.stack +
(cause
? `\nCaused by: \n${indentString(cause.stack ?? cause.message, 2)}`
: '')
);
}
2 changes: 1 addition & 1 deletion packages/nx/src/tasks-runner/run-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { isRelativePath } from '../utils/fileutils';
import { isCI } from '../utils/is-ci';
import { isNxCloudUsed } from '../utils/nx-cloud-utils';
import { output } from '../utils/output';
import { handleErrors } from '../utils/params';
import { handleErrors } from '../utils/handle-errors';
import {
collectEnabledTaskSyncGeneratorsFromTaskGraph,
flushSyncGeneratorChanges,
Expand Down
70 changes: 70 additions & 0 deletions packages/nx/src/utils/handle-errors.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
CreateMetadataError,
ProjectGraphError,
} from '../project-graph/error-types';
import { handleErrors } from './handle-errors';
import { output } from './output';

describe('handleErrors', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('should display project graph error cause message', async () => {
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
await handleErrors(true, async () => {
const cause = new Error('cause message');
const metadataError = new CreateMetadataError(cause, 'test-plugin');
throw new ProjectGraphError(
[metadataError],
{ nodes: {}, dependencies: {} },
{}
);
});
const { bodyLines, title } = spy.mock.calls[0][0];
const body = bodyLines.join('\n');
expect(body).toContain('cause message');
expect(body).toContain('test-plugin');
});

it('should only display wrapper error if not verbose', async () => {
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
await handleErrors(false, async () => {
const cause = new Error('cause message');
const metadataError = new CreateMetadataError(cause, 'test-plugin');
throw new ProjectGraphError(
[metadataError],
{ nodes: {}, dependencies: {} },
{}
);
});

const { bodyLines, title } = spy.mock.calls[0][0];
const body = bodyLines.join('\n');
expect(body).not.toContain('cause message');
});

it('should display misc errors that do not have a cause', async () => {
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
await handleErrors(true, async () => {
throw new Error('misc error');
});
const { bodyLines, title } = spy.mock.calls[0][0];
const body = bodyLines.join('\n');
expect(body).toContain('misc error');
expect(body).not.toMatch(/[Cc]ause/);
});

it('should display misc errors that have a cause', async () => {
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
await handleErrors(true, async () => {
const cause = new Error('cause message');
const err = new Error('misc error', { cause });
throw err;
});
const { bodyLines, title } = spy.mock.calls[0][0];
const body = bodyLines.join('\n');
expect(body).toContain('misc error');
expect(body).toContain('cause message');
});
});
Loading

0 comments on commit 068b066

Please sign in to comment.