decopin-cli

↑ ↓ to navigate · Enter to open · Esc to close

Root-onlyglobal-error.tsx

global-error.tsx

The last error view, when no error.tsx caught it.

global-error.tsx is the end of the error.tsx chain. It receives the same ErrorProps, and it is where a CLI decides how failures look when no subtree had an opinion.

// app/global-error.tsx
import { Line, Text, type ErrorKind, type ErrorProps } from 'decopin-cli';

const USAGE_KINDS: ErrorKind[] = ['validation', 'stdin', 'env'];
const SETUP_KINDS: ErrorKind[] = ['auth', 'missing-tool'];

function headline(kind: ErrorKind): string {
  if (USAGE_KINDS.includes(kind)) return 'Invalid usage';
  if (SETUP_KINDS.includes(kind)) return 'Setup needed';
  return 'Unexpected error';
}

export default function GlobalError({ error, exitCode }: ErrorProps) {
  return (
    <>
      <Line>
        <Text bold color="red">
          {headline(error.kind)}
        </Text>
        {': '}
        {error.issues[0] ?? error.message}
      </Line>
      {error.hints.map((hint) => (
        <Line key={hint}>
          {'  '}
          <Text color="cyan">{hint}</Text>
        </Line>
      ))}
      <Line>
        <Text dim>exit code {exitCode}</Text>
      </Line>
    </>
  );
}

Two things worth keeping in a custom view: error.hints, because they are the commands that fix the problem, and error.issues, because a validation failure can have more than one line.

If global-error.tsx itself throws, the built-in view takes over, so an error view can never turn a failure into silence.