Skip to content
errnode / 1.0.0

errnode 1.0.0

Install from the command line:
Learn more about npm packages
$ npm install @flex-development/errnode@1.0.0
Install via package.json:
"@flex-development/errnode": "1.0.0"

About this version

errnode

npm codecov license conventional commits typescript vitest yarn

Create Node.js errors

Contents

What is this?

This package provides utilities for creating Node.js errors.

When should I use this?

This package is designed to help developers build Node.js tools like ponyfills, as well as more verbose utilities like mlly, by providing a universal API for creating Node.js errors.

Differences between Node.js

Install

This package is ESM only.

yarn add @flex-development/errnode

From Git:

yarn add @flex-development/errnode@flex-development/errnode
See Git - Protocols | Yarn  for details on requesting a specific branch, commit, or tag.

Use

import {
  createNodeError,
  determineSpecificType,
  ErrorCode,
  type MessageFn,
  type NodeError,
  type NodeErrorConstructor
} from '@flex-development/errnode'
import type { OneOrMany } from '@flex-development/tutils'
import assert from 'node:assert'

/**
 * Creates an [`ERR_INVALID_ARG_TYPE`][1] message.
 *
 * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
 *
 * @see https://github.com/nodejs/node/blob/v19.3.0/lib/internal/errors.js#L1197-L1286
 *
 * @param {string} name - Name of invalid argument or property
 * @param {OneOrMany<string>} expected - Expected type(s)
 * @param {unknown} actual - Value supplied by user
 * @return {string} Error message
 */
const msg: MessageFn<[string, OneOrMany<string>, unknown]> = (
  name: string,
  expected: OneOrMany<string>,
  actual: unknown
): string => {
  // ensure name is a string
  assert(typeof name === 'string', "'name' must be a string")

  // ensure expected is an array
  if (!Array.isArray(expected)) expected = [expected]

  /**
   * Primitive value names.
   *
   * Sorted by a rough estimate on most frequently used entries.
   *
   * @const {Set<string>} kTypes
   */
  const kTypes: Set<string> = new Set([
    'string',
    'function',
    'number',
    'object',
    'Function',
    'Object',
    'boolean',
    'bigint',
    'symbol'
  ])

  /**
   * Error message.
   *
   * @var {string} msg
   */
  let msg: string = 'The '

  // stylize invalid argument name
  msg += name.endsWith(' argument')
    ? name
    : `"${name}" ${name.includes('.') ? 'property' : 'argument'}`

  // continue building error message
  msg += ' must be '

  /**
   * Names of expected class instances.
   *
   * @const {string[]} instances
   */
  const instances: string[] = []

  /**
   * Names of other expected types.
   */
  const other: string[] = []

  /**
   * Names of expected primitive types.
   */
  const types: string[] = []

  // get expected types
  for (const value of expected) {
    assert(typeof value === 'string', '`expected` should be of type string[]')

    if (kTypes.has(value)) types.push(value.toLowerCase())
    else if (/^([A-Z][\da-z]*)+$/.exec(value)) instances.push(value)
    else other.push(value === 'object' ? 'Object' : value)
  }

  // special case: handle `object` in case other instances are allowed to
  // outline the differences between each other
  if (instances.length > 0) {
    /**
     * Position of `'object'` in {@linkcode types}.
     *
     * @const {number} pos
     */
    const pos: number = types.indexOf('object')

    // replace 'object' in types with "Object" in instances
    if (pos !== -1) {
      types.splice(pos, 1)
      instances.push('Object')
    }
  }

  // add expected primitive types to error message
  if (types.length > 0) {
    if (types.length > 2) {
      /**
       * Last primitive type name in {@linkcode types}.
       *
       * @const {string} last_type
       */
      const last_type: string = types.pop()!

      msg += `one of type ${types.join(', ')}, or ${last_type}`
    } else if (types.length === 2) {
      msg += `one of type ${types[0]} or ${types[1]}`
    } else {
      msg += `of type ${types[0]}`
    }

    if (instances.length > 0 || other.length > 0) msg += ' or '
  }

  // add expected class instances to error message
  if (instances.length > 0) {
    if (instances.length > 2) {
      /**
       * Last instance type name in {@linkcode instances}.
       *
       * @const {string} last_instance
       */
      const last_instance: string = types.pop()!

      msg += `an instance of ${instances.join(', ')}, or ${last_instance}`
    } else {
      msg += `an instance of ${instances[0]}`
      if (instances.length === 2) msg += ` or ${instances[1]}`
    }

    if (other.length > 0) msg += ' or '
  }

  // add other expected types to error message
  if (other.length > 0) {
    if (other.length > 2) {
      /**
       * Last other type name in {@linkcode other}.
       *
       * @const {string} last_other
       */
      const last_other: string = other.pop()!

      msg += `one of ${other.join(', ')}, or ${last_other}`
    } else if (other.length === 2) {
      msg += `one of ${other[0]} or ${other[1]}`
    } else {
      if (other[0]?.toLowerCase() !== other[0]) msg += 'an '
      msg += `${other[0]}`
    }
  }

  // end error message
  msg += `. Received ${determineSpecificType(actual)}`

  return msg
}

/**
 * [`ERR_INVALID_ARG_TYPE`][1] model.
 *
 * Thrown when an argument of the wrong type was passed to a Node.js API.
 *
 * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
 *
 * @class
 * @implements {NodeError<TypeError>}
 */
const ERR_INVALID_ARG_TYPE: NodeErrorConstructor<
  TypeErrorConstructor,
  typeof msg
> = createNodeError(ErrorCode.ERR_INVALID_ARG_TYPE, TypeError, msg)

/**
 * {@linkcode ERR_INVALID_ARG_TYPE} instance.
 *
 * @const {NodeError<TypeError>} err
 */
const err: NodeError<TypeError> = new ERR_INVALID_ARG_TYPE(
  'path',
  'string',
  null
)

console.error(err)
console.debug('err instanceof TypeError:', err instanceof TypeError)

...running that yields:

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received null
    at new NodeError (file:////home/runner/work/errnode/errnode/src/create-node-error.ts:94:5)
    at file:////home/runner/work/errnode/errnode/scratch.ts:201:35
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:533:24)
    at async loadESM (node:internal/process/esm_loader:91:5)
    at async handleMainPromise (node:internal/modules/run_main:65:12)
err instanceof TypeError: true

API

This package exports the following identifiers:

There is no default export.

createNodeError(code, Base, message)

Creates a Node.js error constructor.

If the given error message is a function, constructor arguments are passed to message. If the message is a string, constructor arguments are passed to util.format instead.

  • {ErrorCode} code — Node.js error code
  • {B extends ErrorConstructor} Base — Error base class
  • {M extends MessageFn | string} message — Error message or message function
  • Returns: {NodeErrorConstructor<B, M>} NodeError constructor

Source: src/create-node-error.ts

determineSpecificType(value)

Determines the specific type of a value for type-mismatch errors.

  • {unknown} value — Value to evaluate
  • Returns: {string} Specific type of value

Source: src/determine-specific-type.ts

Types

This package is fully typed with TypeScript. It exports the following definitions:

Enums

Interfaces

Type Definitions

Contribute

See CONTRIBUTING.md.

Details


Assets

  • errnode-1.0.0.tgz

Download activity

  • Total downloads 5
  • Last 30 days 0
  • Last week 0
  • Today 0