tkrotoff/throw-on

View on GitHub
src/throwOnConsole.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
// Instead of 'node:util' because it doesn't work in the browser
import { format } from './util';

type Options = {
  /**
   * Messages to ignore (won't throw), each message to ignore can be a substring or a regex.
   *
   * Empty list by default.
   */
  ignore?: (string | RegExp)[];
};

type ConsoleMethodName = 'assert' | 'error' | 'warn' | 'info' | 'log' | 'dir' | 'debug';

function formatMessage(ignore: Options['ignore'], ...data: any[]) {
  ignore = ignore ?? [];
  const message = format(...data);

  return {
    shouldThrow: !ignore.some(msgToIgnore =>
      typeof msgToIgnore === 'string' ? message.includes(msgToIgnore) : message.match(msgToIgnore)
    ),
    message
  };
}

const originalConsole = {
  assert: console.assert,
  error: console.error,
  warn: console.warn,
  info: console.info,
  log: console.log,
  dir: console.dir,
  debug: console.debug
};

export class ThrowOnError extends Error {
  constructor(message: string) {
    super(message);

    this.name = 'ThrowOnError';

    // No stack trace
    this.stack = undefined;
  }
}

function getFirstLine(message: string) {
  const lines = message.split('\n');
  return lines.length > 1 ? lines[0] : message;
}

/**
 * Makes console method to throw if called.
 */
export function throwOnConsole(methodName: ConsoleMethodName, options: Options = {}) {
  const { ignore } = options;

  if (methodName === 'assert') {
    console.assert = (condition?: boolean, ...data: any[]) => {
      if (!condition) {
        const { shouldThrow, message } = formatMessage(ignore, ...data);
        if (shouldThrow) {
          throw new ThrowOnError(`throw-on console.assert: ${getFirstLine(message)}`);
        }
      }
    };
  } else {
    console[methodName] = (...data: any[]) => {
      originalConsole[methodName](...data);
      const { shouldThrow, message } = formatMessage(ignore, ...data);
      if (shouldThrow) {
        throw new ThrowOnError(`throw-on console.${methodName}: ${getFirstLine(message)}`);
      }
    };
  }
}

/**
 * Restores the original console method implementation.
 */
export function restoreConsole(methodName: ConsoleMethodName) {
  console[methodName] = originalConsole[methodName];
}