import Debug, { type Debugger } from 'debug'; type LogHandler = { (...args: Parameters): void; extend: (namespace: string) => LogHandler; }; /** * Declares that Logger instances may also be invoked as functions. */ // eslint-disable-next-line export interface Logger { (...args: unknown[]): void; } /** * Logger class, similar to 'debug' library but you can call `instanceof Logger` to check if a value is a Logger instance. */ // eslint-disable-next-line export class Logger { public readonly namespace!: string; private readonly handler!: LogHandler; public constructor(namespace: string, handler: LogHandler = Debug(namespace)) { /** * I'm going to be honest, this file is somewhat an experiment. * The logger from 'debug' is a fancy function with methods on it. * I wanted to extend that functionality to support 'extend' and also determine whether the object is a Logger instance. * This makes it trivial to perform a type check on the logger, since its no longer just a function. But, I wanted to keep the exact same API * as debug, so this uses gross, disgusting, blasphemous prototype methods to assign a function onto this class prototype. */ // Make a function that just calls the 'debug' function with the given arguments const logger = ((...args: Parameters): void => { handler(...args); }) as Logger; // Mutate the logger function to inherit from this class. // This allows us to use the 'instanceof' operator to check if the object is a Logger instance. Object.setPrototypeOf(logger, new.target.prototype); // Add the namespace and the handler to the 'logger' function we defined above // Basically, we are combining this Class with the 'logger' function that we created above. Object.defineProperties(logger, { namespace: { value: namespace, enumerable: true, }, handler: { value: handler, }, }); // Instead of returning the class, we return the 'logger' function we created above. return logger; } public extend(childNamespace: string): Logger { return new Logger(`${this.namespace}:${childNamespace}`, this.handler.extend(childNamespace)); } static isLogger(value: unknown): value is Logger { return value instanceof Logger; } }