UNPKG

@ackplus/react-tanstack-data-table

Version:

A powerful React data table component built with MUI and TanStack Table

204 lines (179 loc) 6.24 kB
/** * Logging utilities for the DataTable package. * * Provides a lightweight wrapper around `console` that can be configured globally * or per-instance to help troubleshoot behaviour in consuming applications. */ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug'; interface ConsoleLike { debug?: (...args: unknown[]) => void; info?: (...args: unknown[]) => void; warn?: (...args: unknown[]) => void; error?: (...args: unknown[]) => void; log?: (...args: unknown[]) => void; } export interface DataTableLoggingOptions { /** * Whether logging should be enabled. */ enabled?: boolean; /** * Minimum level that will be emitted. Defaults to `warn`. */ level?: LogLevel; /** * Prefix prepended to every log message. Defaults to `DataTable`. */ prefix?: string; /** * Optional scope that will be appended after the prefix when present. */ scope?: string; /** * Include an ISO timestamp ahead of each log line. */ includeTimestamp?: boolean; /** * A custom logger implementation. Defaults to `console`. */ logger?: ConsoleLike; } type LoggerInput = boolean | DataTableLoggingOptions; type ResolvedLoggerConfig = Required<Omit<DataTableLoggingOptions, 'logger'>> & { logger: ConsoleLike; }; type LogMethodLevel = Exclude<LogLevel, 'silent'>; const LOG_LEVEL_ORDER: Record<LogLevel, number> = { silent: 0, error: 1, warn: 2, info: 3, debug: 4, }; const defaultConsole: ConsoleLike = typeof console !== 'undefined' ? console : { log: () => undefined, debug: () => undefined, info: () => undefined, warn: () => undefined, error: () => undefined, }; let globalConfig: ResolvedLoggerConfig = { enabled: false, level: 'warn', prefix: 'DataTable', scope: '', includeTimestamp: false, logger: defaultConsole, }; const isLevelEnabled = (level: LogMethodLevel, config: ResolvedLoggerConfig) => { if (!config.enabled) { return false; } return LOG_LEVEL_ORDER[level] <= LOG_LEVEL_ORDER[config.level]; }; const formatPrefix = (level: LogMethodLevel, config: ResolvedLoggerConfig) => { const segments: string[] = []; if (config.prefix) { segments.push(config.prefix); } if (config.scope) { segments.push(config.scope); } const prefix = segments.length > 0 ? `[${segments.join(':')}]` : ''; return config.includeTimestamp ? `[${new Date().toISOString()}]${prefix ? ` ${prefix}` : ''} [${level.toUpperCase()}]` : `${prefix ? `${prefix} ` : ''}[${level.toUpperCase()}]`; }; const getConsoleMethod = (level: LogMethodLevel, logger: ConsoleLike) => { const methodName = level === 'debug' ? 'debug' : level; return logger[methodName] ?? logger.log ?? defaultConsole.log ?? (() => undefined); }; const normaliseInput = (input?: LoggerInput): DataTableLoggingOptions => { if (typeof input === 'boolean') { return { enabled: input }; } return input ?? {}; }; const resolveConfig = ( scope: string | undefined, input?: LoggerInput, parent?: ResolvedLoggerConfig, ): ResolvedLoggerConfig => { const overrides = normaliseInput(input); const base = parent ?? globalConfig; return { enabled: overrides.enabled ?? base.enabled, level: overrides.level ?? base.level, prefix: overrides.prefix ?? base.prefix, scope: overrides.scope ?? scope ?? base.scope ?? '', includeTimestamp: overrides.includeTimestamp ?? base.includeTimestamp, logger: overrides.logger ?? base.logger ?? defaultConsole, }; }; export interface LoggerInstance { debug: (...args: unknown[]) => void; info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; /** * Create a new logger that inherits configuration and extends the scope. */ child: (scope: string, overrides?: LoggerInput) => LoggerInstance; /** * Check whether a level would emit given current configuration. */ isLevelEnabled: (level: LogMethodLevel) => boolean; /** * Access the resolved configuration for inspection. */ config: ResolvedLoggerConfig; } const createLoggerMethods = (config: ResolvedLoggerConfig): Omit<LoggerInstance, 'child' | 'isLevelEnabled' | 'config'> => { const logWithLevel = (level: LogMethodLevel) => { const consoleMethod = getConsoleMethod(level, config.logger); return (...args: unknown[]) => { if (!isLevelEnabled(level, config)) { return; } const prefix = formatPrefix(level, config); consoleMethod(prefix, ...args); }; }; return { debug: logWithLevel('debug'), info: logWithLevel('info'), warn: logWithLevel('warn'), error: logWithLevel('error'), }; }; /** * Create a new logger instance. Configuration cascades from the global config unless overridden. */ export const createLogger = (scope?: string, input?: LoggerInput, parentConfig?: ResolvedLoggerConfig): LoggerInstance => { const resolvedConfig = resolveConfig(scope, input, parentConfig); const methods = createLoggerMethods(resolvedConfig); const child = (childScope: string, overrides?: LoggerInput) => { const combinedScope = childScope ? (resolvedConfig.scope ? `${resolvedConfig.scope}.${childScope}` : childScope) : resolvedConfig.scope; return createLogger(combinedScope, overrides, resolvedConfig); }; return { ...methods, child, isLevelEnabled: (level: LogMethodLevel) => isLevelEnabled(level, resolvedConfig), config: resolvedConfig, }; }; /** * Configure the global logger defaults for every DataTable instance. */ export const configureDataTableLogging = (options: DataTableLoggingOptions) => { globalConfig = resolveConfig(options.scope, options, globalConfig); }; /** * Read the current global logging configuration. */ export const getDataTableLoggingConfig = (): ResolvedLoggerConfig => ({ ...globalConfig });