|
| 1 | +import differ from 'deep-diff'; |
| 2 | + |
| 3 | +const styles = { |
| 4 | + header: 'color: gray; font-weight: lighter;', |
| 5 | + type: 'color: black; font-weight: bold;', |
| 6 | + prevState: 'color: #9E9E9E; font-weight: bold;', |
| 7 | + action: 'color: #03A9F4; font-weight: bold;', |
| 8 | + nextState: 'color: #4CAF50; font-weight: bold;', |
| 9 | + error: 'color: #F20404; font-weight: bold;', |
| 10 | + black: 'color: #000000; font-weight: bold;', |
| 11 | + |
| 12 | + diff: { |
| 13 | + E: { |
| 14 | + style: `color: #2196F3; font-weight: bold`, |
| 15 | + text: 'CHANGED:', |
| 16 | + }, |
| 17 | + N: { |
| 18 | + style: `color: #4CAF50; font-weight: bold`, |
| 19 | + text: 'ADDED:', |
| 20 | + }, |
| 21 | + D: { |
| 22 | + style: `color: #F44336; font-weight: bold`, |
| 23 | + text: 'DELETED:', |
| 24 | + }, |
| 25 | + A: { |
| 26 | + style: `color: #2196F3; font-weight: bold`, |
| 27 | + text: 'ARRAY:', |
| 28 | + }, |
| 29 | + }, |
| 30 | +}; |
| 31 | + |
| 32 | +export const pad = (num, maxLength) => `${num}`.padStart(0, maxLength); |
| 33 | + |
| 34 | +function formatTime(time) { |
| 35 | + return `${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad( |
| 36 | + time.getSeconds(), |
| 37 | + 2, |
| 38 | + )}.${pad(time.getMilliseconds(), 3)}`; |
| 39 | +} |
| 40 | + |
| 41 | +// Use performance API if it's available in order to get better precision |
| 42 | +const timer = typeof performance?.now === 'function' ? performance : Date; |
| 43 | + |
| 44 | +function renderDiff(diff) { |
| 45 | + const { kind, path, lhs, rhs, index, item } = diff; |
| 46 | + |
| 47 | + switch (kind) { |
| 48 | + case 'E': |
| 49 | + return [path.join('.'), lhs, '→', rhs]; |
| 50 | + case 'N': |
| 51 | + return [path.join('.'), rhs]; |
| 52 | + case 'D': |
| 53 | + return [path.join('.')]; |
| 54 | + case 'A': |
| 55 | + return [`${path.join('.')}[${index}]`, item]; |
| 56 | + default: |
| 57 | + return []; |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +const isLoggingEnabled = |
| 62 | + process.env.NODE_ENV !== 'production' || !!localStorage.getItem('debug'); |
| 63 | + |
| 64 | +export function withLogging(reducerFn) { |
| 65 | + if (isLoggingEnabled) { |
| 66 | + return reducerFn; |
| 67 | + } |
| 68 | + |
| 69 | + const supportsGroups = typeof console.groupCollapsed === 'function'; |
| 70 | + |
| 71 | + return (prevState, action) => { |
| 72 | + const started = timer.now(); |
| 73 | + const startedTime = new Date(); |
| 74 | + |
| 75 | + const newState = reducerFn(prevState, action); |
| 76 | + |
| 77 | + const took = timer.now() - started; |
| 78 | + const diff = differ(prevState, newState); |
| 79 | + |
| 80 | + const header = [ |
| 81 | + [ |
| 82 | + `%caction`, |
| 83 | + `%c${action.type}`, |
| 84 | + `%c@ ${formatTime(startedTime)}`, |
| 85 | + `(in ${took.toFixed(2)} ms)`, |
| 86 | + ].join(' '), |
| 87 | + styles.header, |
| 88 | + styles.type, |
| 89 | + styles.header, |
| 90 | + ]; |
| 91 | + |
| 92 | + if (supportsGroups) { |
| 93 | + console.group(...header); |
| 94 | + } else { |
| 95 | + console.log(...header); |
| 96 | + } |
| 97 | + |
| 98 | + console.log('%c prev state %O', styles.prevState, prevState); |
| 99 | + console.log('%c action %O', styles.action, action); |
| 100 | + console.log('%c new state %O', styles.nextState, newState); |
| 101 | + |
| 102 | + if (!diff) { |
| 103 | + console.log( |
| 104 | + '%c diff %cno state change!', |
| 105 | + styles.prevState, |
| 106 | + styles.error, |
| 107 | + ); |
| 108 | + } else { |
| 109 | + if (supportsGroups) { |
| 110 | + console.groupCollapsed(' diff'); |
| 111 | + } else { |
| 112 | + console.log('diff'); |
| 113 | + } |
| 114 | + |
| 115 | + diff.forEach((elem) => { |
| 116 | + console.log( |
| 117 | + `%c ${styles.diff[elem.kind].text}`, |
| 118 | + styles.diff[elem.kind].style, |
| 119 | + ...renderDiff(elem), |
| 120 | + ); |
| 121 | + }); |
| 122 | + |
| 123 | + if (supportsGroups) { |
| 124 | + console.groupEnd(); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + if (supportsGroups) { |
| 129 | + console.groupEnd(); |
| 130 | + } |
| 131 | + |
| 132 | + return newState; |
| 133 | + }; |
| 134 | +} |
0 commit comments