All files utils.ts

66.04% Statements 35/53
73.33% Branches 22/30
66.67% Functions 10/15
66.67% Lines 34/51

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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117      9x             8x 10x     10x 1x   9x     8x     8x     8x             13x 13x     13x     8x         8x                                                 8x 22x   22x   6x         8x 22x     107x 36x   71x 73x 32x     39x       36x     36x 88x 55x   88x 88x         28x         28x    
import { ActionLike, AC, Action, ActionType } from './types';
 
function isSymbol(x: any): x is symbol {
  return (
    typeof x === 'symbol' ||
    (typeof x === 'object' &&
      Object.prototype.toString.call(x) === '[object Symbol]')
  );
}
 
export const isAction = (action: any): action is ActionLike => {
  Iif (!action) {
    return false;
  }
  if (!Array.isArray(action.type) || action.type.length !== 2) {
    return false;
  }
  return isSymbol(action.type[0]) && typeof action.type[1] === 'string';
};
 
export const repeat = (str: string, times: number) =>
  new Array(times + 1).join(str);
 
export const pad = (num: number, maxLength: number) =>
  repeat('0', maxLength - num.toString().length) + num;
 
export const formatTime = (time: Date) =>
  `${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(
    time.getSeconds(),
    2
  )}.${pad(time.getMilliseconds(), 3)}`;
 
export function getDescription(s: symbol) {
  const match = /Symbol\((.+)\)/.exec(s.toString());
  Iif (!match) {
    throw new Error('Empty symbol: ' + s.toString());
  }
  return match[1];
}
 
export const getActionDescription = (action: ActionType) => {
  const [symbol, type] = action;
  return getDescription(symbol) + '/' + type;
};
 
export const logAction = (epicName: string, action: Action) => {
  const gray = 'color: gray; font-weight: lighter;';
  const bold = 'font-weight: bold';
  const boldBlue = 'font-weight: bold; color: blue';
  const boldRed = 'font-weight: bold; color: red';
  const actionType = getActionDescription(action.type);
  const time = formatTime(new Date());
  if (!actionType.startsWith(epicName)) {
    // tslint:disable-next-line:no-console
    console.log(
      `%c epic%c ${epicName}%c:%c${actionType} %c@ ${time}`,
      gray,
      boldBlue,
      gray,
      boldRed,
      gray
    );
  } else {
    // tslint:disable-next-line:no-console
    console.log(`%c epic%c ${actionType} %c@ ${time}`, gray, bold, gray);
  }
};
 
// FROM https://github.com/huynhsamha/js-snakecase/blob/master/index.js
// ISC
export const snakeCase = (str: string) => {
  Iif (!str) return '';
 
  return String(str)
    .replace(/^[^A-Za-z0-9]*|[^A-Za-z0-9]*$/g, '')
    .replace(/([a-z])([A-Z])/g, (m, a, b) => a + '_' + b.toLowerCase())
    .replace(/[^A-Za-z0-9]+|_+/g, '_')
    .toLowerCase();
};
 
export const toArray = <T>(input: T | T[]): T[] =>
  Array.isArray(input) ? input : [input];
 
export function shallowEqual(a: any[] | null, b: any[] | null) {
  if (!a || !b || a.length !== b.length) {
    return false;
  }
  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) {
      return false;
    }
  }
  return true;
}
 
export function memoize(fn: (...args: any[]) => any) {
  let lastArgs: any[] | null = null;
  let lastResult: any[];
 
  return (...args: any[]) => {
    if (!shallowEqual(args, lastArgs)) {
      lastResult = fn(...args);
    }
    lastArgs = args;
    return lastResult;
  };
}
 
export function getACType(ac: AC) {
  Iif (!ac.getType) {
    throw new Error(
      'getType() not defined in Action Creator: ' + ac.toString()
    );
  }
  return ac.getType();
}