All files / packages/logger/src/format/plugins pad-levels.ts

0% Statements 0/36
100% Branches 1/1
100% Functions 1/1
0% Lines 0/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95                                                                                                                                                                                             
import type { TransformFunction } from '../type'
import { configs, LEVEL, MESSAGE } from '../../triple-beam'
 
export interface PadLevelsOptions {
  /**
   * Log levels. Defaults to `configs.npm.levels` from [triple-beam](https://github.com/winstonjs/triple-beam)
   * module.
   */
  levels?: Record<string, number>
  filler?: string
}
 
class Padder {
  paddings: Record<string, string>
  options: PadLevelsOptions
 
  constructor(opts: PadLevelsOptions = { levels: configs.npm.levels }) {
    this.paddings = Padder.paddingForLevels(opts.levels || configs.npm.levels, opts.filler)
    this.options = opts
  }
 
  /**
   * Returns the maximum length of keys in the specified `levels` Object.
   * @param levels Set of all levels to calculate longest level against.
   * @returns Maximum length of the longest level string.
   */
  static getLongestLevel(levels: Record<string, any>) {
    const lvls = Object.keys(levels).map(level => level.length)
    return Math.max(...lvls)
  }
 
  /**
   * Returns the padding for the specified `level` assuming that the
   * maximum length of all levels it's associated with is `maxLength`.
   * @param level Level to calculate padding for.
   * @param filler Repeatable text to use for padding.
   * @param maxLength Length of the longest level
   * @returns Padding string for the `level`
   */
  static paddingForLevel(level: string, filler: string, maxLength: number) {
    const targetLen = maxLength + 1 - level.length
    const rep = Math.floor(targetLen / filler.length)
    const padding = `${filler}${filler.repeat(rep)}`
    return padding.slice(0, targetLen)
  }
 
  /**
   * Returns an object with the string paddings for the given `levels`
   * using the specified `filler`.
   * @param levels Set of all levels to calculate padding for.
   * @param filler Repeatable text to use for padding.
   * @returns Mapping of level to desired padding.
   */
  static paddingForLevels(levels: Record<string, any>, filler: string = ' ') {
    const maxLength = Padder.getLongestLevel(levels)
    return Object.keys(levels).reduce((acc, level) => {
      acc[level] = Padder.paddingForLevel(level, filler, maxLength)
      return acc
    }, {} as Record<string, string>)
  }
 
  /**
   * Prepends the padding onto the `message` based on the `LEVEL` of
   * the `info`. This is based on the behavior of `winston@2` which also
   * prepended the level onto the message.
   *
   * See: https://github.com/winstonjs/winston/blob/2.x/lib/winston/logger.js#L198-L201
   *
   * @param info Logform info object
   * @param opts Options passed along to this instance.
   * @returns Modified logform info object.
   */
  // eslint-disable-next-line unused-imports/no-unused-vars
  transform: TransformFunction<PadLevelsOptions> = (info, opts = {}) => {
    const level = info[LEVEL]
    const paddings = level ? this.paddings[level] : ''
    info.message = `${paddings}${info.message}`
    if (info[MESSAGE]) {
      info[MESSAGE] = `${paddings}${info[MESSAGE]}`
    }
 
    return info
  }
}
 
/*
 * function padLevels (info)
 * Returns a new instance of the padLevels Format which pads
 * levels to be the same length. This was previously exposed as
 * { padLevels: true } to transports in `winston < 3.0.0`.
 */
export default (opts: PadLevelsOptions) => new Padder(opts)
 
export { Padder }