solid-error

Print meaningful errors to the terminal.

Usage no npm install needed!

<script type="module">
  import solidError from 'https://cdn.skypack.dev/solid-error';
</script>

README

NPM

Stories in Ready bitHound Dependencies bitHound Code codecov Build Status

SolidError

A simple javascript class to print meaningful errors, inspired by elm's error verbosity.

SolidError screenshot

Installation

Install SolidError using NPM:

npm install --save solid-error

What it's useful for

SolidError comes in handy whenever you need to present meaningful, consistent errors to your users in a javascript console application.

It can be used to log custom errors and to wrap unexpected system errors, giving a coherent, customizable look to your errors throughout your program.

It accept external Error Definition files that will be automatically mapped to your custom errors to help you keep your code organized, plus it support multiple languages.

How it works

At its core SolidError is an Error subclass so you can threat it as normal Error instance. You can log it, throw it, read its stack trace, the usual.

However, when logged using the logError utility method, SolidError will render an in-depth, formatted, error explanation to the console that follows the convention:

==== ERROR: <Human readable error name> ========================

<The Error's message>

<SolidError's error explanation>

---- HINTS: ----------------------------------------------------

<SolidError's hints section>

----------------------------------------------------------------
Code: <The Error's code>
[Path: <The Error's file or directory path>]
----------------------------------------------------------------

Examples

See: Running the examples

Basic Error

The most basic error you can create looks like this:

import { SolidError, logError }

logError( new SolidError( 'Something went wrong.' ) )

Which will print:

basic error output

Creating a custom error

To provide your users with more informations regarding your error, create an error definition to describe it.

Supply a short and a long explanation for the cause and help them resolve the issue by giving them some hints. Use markdown syntax to format and highlight your text.

Read Using external Error Definitions to learn how to use external definitions.

import { SolidError, logError } from 'solid-error'

// Create an error definition
// that describe the cause of error
const errProps = {
  code: 'ECNF',
  errno: -500,
  path: '/etc/awesome.cfg'
  name: 'ConfigurationNotFoundError',
  readableName: 'Configuration Not Found',
  message: 'Configuration file not found.',
  explain: 'An expected configuration file for this application was not found '
    + 'at path `/etc/awesome.cfg`. This could happen if the file was moved or '
    + 'deleted.\n\nPlease restore the file.',
  hints: 'To restore the file from a previous backup:\n\n'
    + '```bash\n'
    + '$ cp /etc/awesome.bak /etc/awesome.cfg\n'
    + '$ awesome --checkcfg /etc/awesome.cfg\n'
    + '```\n\n'
    + 'To generate a new configuration file:\n\n'
    + '```bash\n'
    + '$ awesome --initConfig'
    + '```'
}

const err = new SolidError( errProps.describe, errProps )
logError( err )

Once logged, your solid error will look like:

custom errors

Wrapping errors

Sometimes you may need to throw errors that are unknown and unexpected. Wrapping them in a SolidError will automatically format the errors for you giving your users a consistent experience.

import fs from 'fs'
import { SolidError, logError } from 'solid-error'

fs.readFile( '/non/existent/file', ( err, data ) => {
  if ( err ) {
    const readErr = new SolidError( err )
    logError( readErr )
  }
})

wrapped errors

Using external Error Definitions

Defining Error Definitions manually in code can be a tedious task. Besides, you may have custom errors already defined in your code.

class ConfigurationNotFoundError extends Error {
  constructor( ...args ) {
    super( args )
    this.name = 'ConfigurationNotFoundError'
    this.message = 'Configuration file not found.'
  }
}

To keep your code clean and organized you can define external error definitions that will map one-to-one with your error's name.

Create a directory to host your error definitions (SolidError support multiple languages, so by default all definitions should be at least provided for the english language):

mkdir -p ./errdef/en

Describe your error in a ConfigurationNotFoundError.yaml file, and save it under ./errdef/en/

code : ECNF
errno : -500
name : ConfigurationNotFoundError
readablName : Configuration Not Found
path : /etc/awesome.cfg
explain : >
  An expected configuration file for this application was not found
  at path `/etc/awesome.cfg`. This could happen if the file was moved or
  deleted.

  Please restore the file.
hints : >
  To restore the file from a previous backup:

    $ cp /etc/awesome.bak /etc/awesome.cfg
    $ awesome --checkcfg /etc/awesome.cfg

  To generate a new configuration file:

    $ awesome --initConfig

Add the error definitions directory to the options:

import solidErr, { SolidError, logError } from 'solid-error'

solidErr.setOptions({
  includes: [ './errdef' ]
})

Now, when your custom error gets logged,

try {
  getConfig() // throws a ConfigurationNotFoundError
}
catch ( configErr ) {
  logError( new SolidError( configErr ) )
}

Solid Error will print its definition:

external definitions

Customizing output appearance

Use setStyles to define SolidError's output appearance.

import solidErr, { SolidError, logError } from 'solid-error'

solidError.setStyles({
  marginRight: 0,        // set margin right to 0 (default: 2)
  columns: 55,           // width reduced to 55 columns
  wordwrap: true,        // enable wordwrap (default)
  headerColor: 'red',    // set header color to red
  headerStyle: '—',      // set header style to em-dash
  headerTitle: 'OOPS',   // change header title prefix
  messageColor: 'cyan',  // set description message to cyan
  hintsColor: 'green',   // set the example color to green
  hintsStyle: '—',       // change example style to em-dash
  hintsTitle: 'HINTS',   // change example title prefix
  footer: 'red',         // set footer color to red
  footerStyle: '—',      // set footer style to em-dash
})

const errProps = {
  code: 'ESTYL',
  errno: -1,
  name: 'ExampleStyleError',
  readableName: 'Example Style Error',
  message: 'Just an example error to show appeareance customization',
  explain: 'This error was custom created to test **SolidError** style '
    + 'customization.\n\nHeader and footer should be **red** '
    + 'while the error description should be **cyan**. Also, section divider\'s'
    + ' styles should look different.',
  example: 'Example section should be called _`HINTS`_ now.'
}

logError( new SolidError( errProps.describe, errProps ) )

Your solid error will now look something like:

custom look

Custom Renderers

You can opt-out from the default style by providing a custom renderer.

import MyCustomRender from './myRenderer'
import solidErr from 'solid-error'

solidErr.setOptions({
  renderer: new MyCustomRender()
})

Take a look at SolidRender, the default renderer, and read the API to learn how to write your custom renderer.

Using custom languages

You can easly support internazionalization with multiple language translations. In addition to translate your custom errors, you can override the default error definitions by naming your definition like a class error or a syserror code.

Read Using external Error Definitions to learn how to setup external definitions.

To override the default ENOENT SystemError definition, for example, create a file named ENOENT.yaml in your target language directory. To override ENOENT for the Italian language, save you file to ./errdef/it/ENOENT.yaml

Define your translated error:

```yaml
code  : ENOENT
errno : -2
name  : FileOrDirectoryNotFoundError
readableName : File o Directory Non Trovata
message : File o directory non trovata al percorso indicato.
explain : >
Tipicamente invocato da operazioni che coinvolgono il file system per
indicare che un componente al percorso specificato non esiste.
```

Then simply change the language from the options and add your custom error definitions directory to the additional directories:

```javascript
import fs from 'fs'
import path from 'path'
import solidErr, { SolidError, logError } from 'solid-error'

// Path that contains your Error Definitions files
const customErrPath = path.join( __dirname, './definitions' )

solidErr.setOptions({
  lang: 'it',                   // set the language to 'it'
  includes: [ customErrPath ]
})

try {
  // raise ENOENT (file or directory not found ) error
  fs.readFileSync( '/non/existent/file' )
}
catch( readErr ) {
  // log the translated error.
  logError( new SolidError( readErr ) )
}
```

Now, when a ENOENT system error gets logged, you will get the transalted version of the error:

translations

API

Here's your API, you nerd.

HISTORY

Review the change log, if you're into that stuff! 🕵

CREDITS

SolidError is written and mantained by Roberto Mauro.

LICENSE

SolidError is released under the MIT License. For more informations read the LICENSE file.