From 1dc818f51ffdcff1f9f74c7a4da826b5d3d32d3f Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Thu, 10 Feb 2022 16:30:53 +0000 Subject: [PATCH] Add pinia-logger --- web/src/main.ts | 6 ++- web/src/pinia-logger.ts | 82 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 web/src/pinia-logger.ts diff --git a/web/src/main.ts b/web/src/main.ts index b2fa505..0ba7fd3 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -7,10 +7,14 @@ import { useBudgetsStore } from './stores/budget'; import { useSessionStore } from './stores/session'; import { useSettingsStore } from './stores/settings'; import { useAccountStore } from './stores/budget-account' +import PiniaLogger from './pinia-logger' const app = createApp(App) app.use(router) -app.use(createPinia()) + +const pinia = createPinia() +pinia.use(PiniaLogger()) +app.use(pinia) app.mount('#app') router.beforeEach(async (to, from, next) => { diff --git a/web/src/pinia-logger.ts b/web/src/pinia-logger.ts new file mode 100644 index 0000000..eb917f3 --- /dev/null +++ b/web/src/pinia-logger.ts @@ -0,0 +1,82 @@ +import { PiniaPluginContext, StoreGeneric, _ActionsTree, _StoreOnActionListenerContext } from 'pinia'; + +const cloneDeep = (obj: T): T => { + try { + return JSON.parse(JSON.stringify(obj)); + } catch { + return { ...obj }; + } +}; +const formatTime = (date = new Date()) => { + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + const seconds = date.getSeconds().toString().padStart(2, '0'); + const milliseconds = date.getMilliseconds().toString(); + + return `${hours}:${minutes}:${seconds}:${milliseconds}`; +}; + +export interface PiniaLoggerOptions { + disabled?: boolean; + expanded?: boolean; + showDuration?: boolean + showStoreName?: boolean; + logErrors?: boolean; +} + +export type PiniaActionListenerContext = _StoreOnActionListenerContext; + +const defaultOptions: PiniaLoggerOptions = { + logErrors: true, + disabled: false, + expanded: true, + showStoreName: true, + showDuration: false, +}; + +export const PiniaLogger = (config = defaultOptions) => (ctx: PiniaPluginContext) => { + const options = { + ...defaultOptions, + ...config, + }; + + if (options.disabled) return; + + + ctx.store.$onAction((action: PiniaActionListenerContext) => { + const startTime = Date.now(); + const prevState = cloneDeep(ctx.store.$state); + + const log = (isError?: boolean, error?: any) => { + const endTime = Date.now(); + const duration = endTime - startTime + 'ms'; + const nextState = cloneDeep(ctx.store.$state); + const storeName = action.store.$id; + const title = `action 🍍 ${options.showStoreName ? `[${storeName}] ` : ''}${action.name} ${isError ? `failed after ${duration} ` : ''}@ ${formatTime()}`; + + console[options.expanded ? 'group' : 'groupCollapsed'](`%c${title}`, `font-weight: bold; ${isError ? 'color: #ed4981;' : ''}`); + console.log('%cprev state', 'font-weight: bold; color: grey;', prevState); + console.log('%caction', 'font-weight: bold; color: #69B7FF;', { + type: action.name, + args: action.args.length > 0 ? { ...action.args } : undefined, + ...(options.showStoreName && { store: action.store.$id }), + ...(options.showDuration && { duration }), + ...(isError && { error }), + }); + console.log('%cnext state', 'font-weight: bold; color: #4caf50;', nextState); + console.groupEnd(); + }; + + action.after(() => { + log(); + }); + + if (options.logErrors) { + action.onError((error) => { + log(true, error); + }); + } + }); +}; + +export default PiniaLogger; \ No newline at end of file