@drainpixie/pika
Version:
a cute and shiny logger
1 lines • 15.1 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/utils.ts"],"sourcesContent":["import { parse } from \"node:path\";\nimport {\n Colour,\n Figure,\n Level,\n type LogType,\n default as TYPES,\n colour,\n shouldUseColors,\n} from \"./utils\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * The options for a Pika instance.\n */\nexport interface PikaOptions {\n /**\n * The scope of the logger.\n */\n readonly scope: string;\n\n /**\n * The level of the logger.\n */\n readonly level: Level;\n\n /**\n * The secrets to filter.\n */\n readonly secrets: readonly string[];\n\n /**\n * Whether to use colors.\n */\n readonly useColors: boolean;\n}\n\n/**\n * A Pika logger.\n */\ntype PikaLogMethod = (...args: readonly unknown[]) => void;\n\n/**\n * A Pika logger.\n */\nexport class Pika {\n readonly #scope: string;\n readonly #level: Level;\n readonly #longest: number;\n readonly #useColors: boolean;\n readonly #secrets: readonly string[];\n\n /**\n * Creates a new instance of Pika.\n * @param options The options for the instance.\n */\n constructor(options: Partial<PikaOptions> = {}) {\n this.#scope = options.scope ?? this.#getFilename();\n this.#level = options.level ?? Level.INFO;\n this.#longest = this.#getLongestLabel();\n this.#secrets = Object.freeze([...(options.secrets ?? [])]);\n this.#useColors = options.useColors ?? shouldUseColors();\n }\n\n /**\n * Clones the current instance with the given scope.\n * @param scope The scope to set.\n * @returns A new instance with the given scope.\n */\n scope(scope: string) {\n return new Pika({\n ...this.options,\n scope,\n });\n }\n\n /**\n * Clones the current instance with the given level.\n * @param level The level to set.\n * @returns A new instance with the given level.\n */\n level(level: Level) {\n return new Pika({\n ...this.options,\n level,\n });\n }\n\n /**\n * Clones the current instance with the given secrets.\n * @param secrets The secrets to add.\n * @returns A new instance with the given secrets.\n */\n secrets(...secrets: readonly string[]) {\n return new Pika({\n ...this.options,\n secrets: [...this.#secrets, ...secrets],\n });\n }\n\n /**\n * Returns the current options.\n * @returns The current options.\n */\n get options(): Partial<PikaOptions> {\n return {\n scope: this.#scope,\n level: this.#level,\n secrets: this.#secrets,\n useColors: this.#useColors,\n };\n }\n\n #getFilename() {\n const originalPrepareStackTrace = Error.prepareStackTrace;\n\n try {\n Error.prepareStackTrace = (_, stack) => stack;\n\n const error = new Error();\n const stack = error.stack as unknown as NodeJS.CallSite[];\n\n if (!stack?.length) return \"anonymous\";\n\n const currentFile = stack[0].getFileName();\n const externalCaller = stack\n .slice(1)\n .find((callSite) => callSite.getFileName() !== currentFile);\n\n if (!externalCaller) return \"anonymous\";\n\n const filePath = externalCaller.getFileName();\n if (!filePath) return \"anonymous\";\n\n return parse(\n filePath.startsWith(\"file://\") ? fileURLToPath(filePath) : filePath,\n ).name;\n } catch {\n return \"anonymous\";\n } finally {\n Error.prepareStackTrace = originalPrepareStackTrace;\n }\n }\n\n #getLongestLabel() {\n return Math.max(\n ...Object.values(TYPES)\n .map((type) => type.label?.length ?? 0)\n .filter((length) => length > 0),\n 0,\n );\n }\n\n #filterSecrets(input: string) {\n if (this.#secrets.length === 0) return input;\n\n return this.#secrets.reduce(\n (sanitized, secret) => sanitized.replaceAll(secret, \"[REDACTED]\"),\n input,\n );\n }\n\n #formatScope() {\n return `[${this.#scope}]`;\n }\n\n #getMeta() {\n const meta = [this.#formatScope()];\n\n if (meta.length > 0) {\n meta.push(Figure.POINTER_SMALL);\n return meta.map((item) => colour(item, Colour.GREY));\n }\n\n return meta;\n }\n\n #applyColor(text: string, ...colors: Colour[]) {\n return this.#useColors ? colour(text, ...colors) : text;\n }\n\n #formatError(error: Error) {\n if (!error.stack) return error.message;\n\n const [name, ...stackLines] = error.stack.split(\"\\n\");\n return `${this.#applyColor(name, Colour.UNDERLINE)}\\n${this.#applyColor(stackLines.join(\"\\n\"), Colour.GREY)}`;\n }\n\n #formatObject(obj: Record<string, unknown>) {\n return Object.entries(obj)\n .map(([key, value]) => `${key} = ${JSON.stringify(value)}`)\n .join(\", \");\n }\n\n #buildMessage(type: LogType, ...args: readonly unknown[]) {\n const parts = [...this.#getMeta()];\n const { label, colour: typeColour, badge } = type;\n\n parts.push(this.#applyColor(`${badge} `, typeColour));\n parts.push(\n `${this.#applyColor(label, typeColour, Colour.UNDERLINE)}${\" \".repeat(Math.max(0, this.#longest - label.length))}`,\n );\n\n if (args.length === 0) return parts.join(\" \");\n\n const [first, ...rest] = args;\n\n if (first instanceof Error) parts.push(this.#formatError(first));\n else if (\n typeof first === \"object\" &&\n first !== null &&\n !(first instanceof Date) &&\n !(first instanceof RegExp)\n )\n parts.push(this.#formatObject(first as Record<string, unknown>));\n else parts.push([first, ...rest].map((arg) => String(arg)).join(\" \"));\n\n return parts.join(\" \");\n }\n\n #log(type: LogType, ...args: readonly unknown[]) {\n if (type.level < this.#level) return;\n type.stream.write(\n `${this.#filterSecrets(this.#buildMessage(type, ...args))}\\n`,\n );\n }\n\n /**\n * Clones the current instance with the given options.\n * @param overrides The options to override.\n * @returns A new instance with the given options.\n */\n clone(overrides: Partial<PikaOptions> = {}): Pika {\n return new Pika({\n ...this.options,\n ...overrides,\n });\n }\n\n readonly success: PikaLogMethod = (...args) =>\n this.#log(TYPES.success, ...args);\n readonly error: PikaLogMethod = (...args) => this.#log(TYPES.error, ...args);\n readonly fatal: PikaLogMethod = (...args) => this.#log(TYPES.fatal, ...args);\n readonly trace: PikaLogMethod = (...args) => this.#log(TYPES.trace, ...args);\n readonly debug: PikaLogMethod = (...args) => this.#log(TYPES.debug, ...args);\n readonly warn: PikaLogMethod = (...args) => this.#log(TYPES.warn, ...args);\n readonly info: PikaLogMethod = (...args) => this.#log(TYPES.info, ...args);\n}\n\n/**\n * Creates a new instance of Pika.\n * @param options The options for the instance.\n * @returns A new instance of Pika.\n */\nexport const pika = (options: Partial<PikaOptions> = {}) => new Pika(options);\nexport * from \"./utils\";\n","import { WriteStream } from \"node:tty\";\n\n/**\n * The available figures.\n */\nexport const Figure = {\n TICK: \"✓\",\n INFO: \"i\",\n CROSS: \"×\",\n WARNING: \"!\",\n ELLIPSIS: \"…\",\n POINTER_SMALL: \"›\",\n} as const;\n\n/**\n * The available colours.\n */\nexport const Colour = {\n RED: \"\\x1b[31m\",\n BOLD: \"\\x1b[1m\",\n GREY: \"\\x1b[0;37m\",\n BLUE: \"\\x1b[34m\",\n CYAN: \"\\x1b[36m\",\n RESET: \"\\x1b[0m\",\n GREEN: \"\\x1b[32m\",\n YELLOW: \"\\x1b[33m\",\n MAGENTA: \"\\x1b[35m\",\n UNDERLINE: \"\\x1b[4m\",\n} as const;\n\n/**\n * The available levels.\n */\nexport const Level = {\n TRACE: 0,\n DEBUG: 1,\n INFO: 2,\n WARN: 3,\n ERROR: 4,\n FATAL: 5,\n} as const;\n\n/**\n * The available figures.\n */\nexport type Figure = (typeof Figure)[keyof typeof Figure];\n\n/**\n * The available colours.\n */\nexport type Colour = (typeof Colour)[keyof typeof Colour];\n\n/**\n * The available levels.\n */\nexport type Level = (typeof Level)[keyof typeof Level];\n\nexport type LogTypeName =\n | \"success\"\n | \"warn\"\n | \"error\"\n | \"fatal\"\n | \"trace\"\n | \"debug\"\n | \"info\";\n\nexport interface LogType {\n readonly label: string;\n readonly level: Level;\n readonly badge: Figure | string;\n readonly colour: Colour;\n readonly stream: WriteStream | NodeJS.WriteStream;\n}\n\nconst createLogType = (\n label: LogType[\"label\"],\n level: LogType[\"level\"],\n badge: LogType[\"badge\"],\n colour: LogType[\"colour\"],\n stream: LogType[\"stream\"] = process.stdout,\n): LogType => Object.freeze({ label, level, badge, colour, stream });\n\nexport const colour = (input: string, ...colours: readonly Colour[]): string =>\n `${colours.join(\"\")}${input}${Colour.RESET}`;\n\nexport function shouldUseColors() {\n if (process.env.NO_COLOR || process.env.NODE_DISABLE_COLORS) return false;\n if (process.env.FORCE_COLOR) return true;\n\n return process.stdout.isTTY ?? false;\n}\n\nexport default {\n success: createLogType(\"success\", Level.INFO, Figure.TICK, Colour.GREEN),\n warn: createLogType(\"warn\", Level.WARN, Figure.WARNING, Colour.YELLOW),\n error: createLogType(\n \"error\",\n Level.ERROR,\n Figure.CROSS,\n Colour.RED,\n process.stderr,\n ),\n fatal: createLogType(\n \"fatal\",\n Level.FATAL,\n Figure.CROSS,\n Colour.RED,\n process.stderr,\n ),\n trace: createLogType(\"trace\", Level.TRACE, Figure.ELLIPSIS, Colour.BLUE),\n debug: createLogType(\"debug\", Level.DEBUG, Figure.INFO, Colour.CYAN),\n info: createLogType(\"info\", Level.INFO, Figure.INFO, Colour.BLUE),\n} as const satisfies Record<LogTypeName, LogType>;\n"],"mappings":"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","names":["index_exports","__export","Colour","Figure","Level","Pika","colour","pika","shouldUseColors","__toCommonJS","import_node_path","Figure","Colour","Level","createLogType","label","level","badge","colour","stream","input","colours","shouldUseColors","utils_default","import_node_url","Pika","_Pika","#scope","#level","#longest","#useColors","#secrets","options","#getFilename","Level","#getLongestLabel","shouldUseColors","scope","level","secrets","originalPrepareStackTrace","_","stack","currentFile","externalCaller","callSite","filePath","utils_default","type","length","#filterSecrets","input","sanitized","secret","#formatScope","#getMeta","meta","Figure","item","colour","Colour","#applyColor","text","colors","#formatError","error","name","stackLines","#formatObject","obj","key","value","#buildMessage","args","parts","label","typeColour","badge","first","rest","arg","#log","overrides","pika"]}