Files
ansiblings/packages/nopy/src/nopy.errors.ts
T

51 lines
1.9 KiB
TypeScript

/**
* The errors that are the user's to fix.
* @module nopy.errors
*/
/**
* A run that failed for a reason the user can act on: no config file, a cube
* that does not exist, a required variable nothing supplied, a session file
* that will not load.
*
* The point is the *presentation*, not the control flow — nothing catches this
* to recover. A stack trace through `dist/` says nothing useful about a missing
* `.nopyrc.json`, and printing one invites the reader to look for a bug in nopy
* instead of a typo in their project. The CLI prints the message alone and keeps
* the stack behind `NOPY_DEBUG`.
*
* Mirrors keyman's `UsageError` deliberately: the two CLIs are kept in step on
* how they fail for the same reason their update modules are duplicated rather
* than shared.
*/
export class NopyUsageError extends Error {
constructor(message: string) {
super(message);
this.name = 'NopyUsageError';
}
}
/**
* Reports a failed run in as many lines as it deserves.
*
* A {@link NopyUsageError} prints as one line: it is something the reader can
* fix, and three frames into `dist/` say nothing about a missing `.nopyrc.json`
* except that it looks like a crash in nopy rather than a typo in the project.
* Everything else keeps its stack, because an unexpected failure is exactly when
* one is worth having. `NOPY_DEBUG` forces it for both.
*
* Lives here rather than in `nopy.cli.ts` because the CLI is excluded from
* coverage — it is argv wiring, and this is a decision.
*/
export function reportError(error: unknown): void {
const message = error instanceof Error ? error.message : String(error);
console.error(`Error: ${message}`);
const stack = error instanceof Error ? error.stack : undefined;
const wanted = process.env.NOPY_DEBUG || !(error instanceof NopyUsageError);
if (wanted && stack) console.error(stack);
else if (!process.env.NOPY_DEBUG) console.error('Set NOPY_DEBUG=1 for the full stack trace.');
}