Skip to content

Commit c9d042c

Browse files
committed
chore: improve logger with styles
1 parent d91ab0e commit c9d042c

File tree

4 files changed

+98
-64
lines changed

4 files changed

+98
-64
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './config'
22
export * from './format'
33
export { Logger, logger } from './logger'
4+
export * from './style'
45
export * from './types'
56
export * from './utils'

src/logger.ts

Lines changed: 40 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,14 @@ import { join } from 'node:path'
77
import process from 'node:process'
88
import { pipeline } from 'node:stream/promises'
99
import { createGzip } from 'node:zlib'
10-
1110
import { config as defaultConfig } from './config'
1211
import { JsonFormatter } from './formatters/json'
12+
import { bgRed, bgYellow, blue, bold, green, styles, white } from './style'
1313
import { isBrowserProcess } from './utils'
1414

1515
// Define missing types
1616
type BunTimer = ReturnType<typeof setTimeout>
1717

18-
// Add terminal styling utilities
19-
const terminalStyles = {
20-
// Text colors
21-
red: (text: string) => `\x1B[31m${text}\x1B[0m`,
22-
green: (text: string) => `\x1B[32m${text}\x1B[0m`,
23-
yellow: (text: string) => `\x1B[33m${text}\x1B[0m`,
24-
blue: (text: string) => `\x1B[34m${text}\x1B[0m`,
25-
magenta: (text: string) => `\x1B[35m${text}\x1B[0m`,
26-
cyan: (text: string) => `\x1B[36m${text}\x1B[0m`,
27-
white: (text: string) => `\x1B[37m${text}\x1B[0m`,
28-
gray: (text: string) => `\x1B[90m${text}\x1B[0m`,
29-
30-
// Background colors
31-
bgRed: (text: string) => `\x1B[41m${text}\x1B[0m`,
32-
bgYellow: (text: string) => `\x1B[43m${text}\x1B[0m`,
33-
34-
// Text styles
35-
bold: (text: string) => `\x1B[1m${text}\x1B[0m`,
36-
dim: (text: string) => `\x1B[2m${text}\x1B[0m`,
37-
italic: (text: string) => `\x1B[3m${text}\x1B[0m`,
38-
underline: (text: string) => `\x1B[4m${text}\x1B[0m`,
39-
40-
// Reset
41-
reset: '\x1B[0m',
42-
}
43-
44-
// Log level icons/badges similar to consola
45-
const levelIcons = {
46-
debug: '🔍',
47-
info: terminalStyles.blue('ℹ'),
48-
success: terminalStyles.green('✓'),
49-
warning: terminalStyles.bgYellow(terminalStyles.white(terminalStyles.bold(' WARN '))), // Use badge style for warnings
50-
error: terminalStyles.bgRed(terminalStyles.white(terminalStyles.bold(' ERROR '))), // Use badge style for errors
51-
}
52-
5318
interface FingersCrossedConfig {
5419
activationLevel: LogLevel
5520
bufferSize: number
@@ -85,6 +50,15 @@ interface NetworkError extends Error {
8550
code?: string
8651
}
8752

53+
// Log level icons/badges similar to consola
54+
const levelIcons = {
55+
debug: '🔍',
56+
info: blue('ℹ'),
57+
success: green('✓'),
58+
warning: bgYellow(white(bold(' WARN '))), // Use badge style for warnings
59+
error: bgRed(white(bold(' ERROR '))), // Use badge style for errors
60+
}
61+
8862
export class Logger {
8963
private name: string
9064
private fileLocks: Map<string, number> = new Map()
@@ -857,7 +831,7 @@ export class Logger {
857831
}
858832

859833
private formatConsoleTimestamp(date: Date): string {
860-
return this.fancy ? terminalStyles.gray(date.toLocaleTimeString()) : date.toLocaleTimeString()
834+
return this.fancy ? styles.gray(date.toLocaleTimeString()) : date.toLocaleTimeString()
861835
}
862836

863837
private formatConsoleMessage(parts: { timestamp: string, icon?: string, tag?: string, message: string, level?: LogLevel, showTimestamp?: boolean }): string {
@@ -898,7 +872,7 @@ export class Logger {
898872
}
899873
else {
900874
// For other levels, apply standard formatting
901-
mainPart = `${icon} ${tag} ${terminalStyles.cyan(message)}`
875+
mainPart = `${icon} ${tag} ${styles.cyan(message)}`
902876
}
903877

904878
// If we don't need to show timestamp, just return the message
@@ -957,9 +931,6 @@ export class Logger {
957931
}
958932

959933
private async log(level: LogLevel, message: string | Error, ...args: any[]): Promise<void> {
960-
if (!this.shouldLog(level))
961-
return
962-
963934
const timestamp = new Date()
964935
const consoleTime = this.formatConsoleTimestamp(timestamp)
965936
const fileTime = this.formatFileTimestamp(timestamp)
@@ -979,7 +950,7 @@ export class Logger {
979950
// Format console output
980951
if (this.fancy && !isBrowserProcess()) {
981952
const icon = levelIcons[level]
982-
const tag = this.options.showTags !== false && this.name ? terminalStyles.gray(this.formatTag(this.name)) : ''
953+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : ''
983954

984955
// Format the console output based on log level
985956
let consoleMessage: string
@@ -989,7 +960,7 @@ export class Logger {
989960
timestamp: consoleTime,
990961
icon,
991962
tag,
992-
message: terminalStyles.gray(formattedMessage),
963+
message: styles.gray(formattedMessage),
993964
level,
994965
})
995966
console.error(consoleMessage)
@@ -1009,7 +980,7 @@ export class Logger {
1009980
timestamp: consoleTime,
1010981
icon,
1011982
tag,
1012-
message: terminalStyles.green(formattedMessage),
983+
message: styles.green(formattedMessage),
1013984
level,
1014985
})
1015986
console.error(consoleMessage)
@@ -1040,7 +1011,7 @@ export class Logger {
10401011
if (line.trim() && !line.includes(formattedMessage)) {
10411012
console.error(this.formatConsoleMessage({
10421013
timestamp: consoleTime,
1043-
message: terminalStyles.gray(` ${line}`),
1014+
message: styles.gray(` ${line}`),
10441015
level,
10451016
showTimestamp: false, // Don't show timestamp for stack traces
10461017
}))
@@ -1058,11 +1029,15 @@ export class Logger {
10581029
}
10591030
}
10601031

1032+
if (!this.shouldLog(level))
1033+
return
1034+
10611035
// Create the log entry for file logging
10621036
let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}\n`
10631037
if (errorStack) {
10641038
logEntry += `${errorStack}\n`
10651039
}
1040+
logEntry = logEntry.replace(this.ANSI_PATTERN, '')
10661041

10671042
// Write to file
10681043
await this.writeToFile(logEntry)
@@ -1078,13 +1053,13 @@ export class Logger {
10781053

10791054
// Show start message with spinner-like indicator
10801055
if (this.fancy && !isBrowserProcess()) {
1081-
const tag = this.options.showTags !== false && this.name ? terminalStyles.gray(this.formatTag(this.name)) : ''
1056+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : ''
10821057
const consoleTime = this.formatConsoleTimestamp(new Date())
10831058
console.error(this.formatConsoleMessage({
10841059
timestamp: consoleTime,
1085-
icon: terminalStyles.blue('◐'),
1060+
icon: styles.blue('◐'),
10861061
tag,
1087-
message: `${terminalStyles.cyan(label)}...`,
1062+
message: `${styles.cyan(label)}...`,
10881063
}))
10891064
}
10901065

@@ -1108,13 +1083,14 @@ export class Logger {
11081083
logEntry += ` ${JSON.stringify(metadata)}`
11091084
}
11101085
logEntry += '\n'
1086+
logEntry = logEntry.replace(this.ANSI_PATTERN, '')
11111087

11121088
// Show completion message with tag based on showTags option
11131089
if (this.fancy && !isBrowserProcess()) {
1114-
const tag = this.options.showTags !== false && this.name ? terminalStyles.gray(this.formatTag(this.name)) : ''
1090+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : ''
11151091
console.error(this.formatConsoleMessage({
11161092
timestamp: consoleTime,
1117-
icon: terminalStyles.green('✓'),
1093+
icon: styles.green('✓'),
11181094
tag,
11191095
message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ''}`,
11201096
}))
@@ -1367,24 +1343,24 @@ export class Logger {
13671343
if (this.options.showTags !== false && this.name) {
13681344
console.error(this.formatConsoleMessage({
13691345
timestamp: consoleTime,
1370-
message: terminalStyles.gray(this.formatTag(this.name)),
1346+
message: styles.gray(this.formatTag(this.name)),
13711347
showTimestamp: false,
13721348
}))
13731349
}
13741350

13751351
// Show timestamp only on the first line of the box
13761352
console.error(this.formatConsoleMessage({
13771353
timestamp: consoleTime,
1378-
message: terminalStyles.cyan(top),
1354+
message: styles.cyan(top),
13791355
}))
13801356
boxedLines.forEach(line => console.error(this.formatConsoleMessage({
13811357
timestamp: consoleTime,
1382-
message: terminalStyles.cyan(line),
1358+
message: styles.cyan(line),
13831359
showTimestamp: false,
13841360
})))
13851361
console.error(this.formatConsoleMessage({
13861362
timestamp: consoleTime,
1387-
message: terminalStyles.cyan(bottom),
1363+
message: styles.cyan(bottom),
13881364
showTimestamp: false,
13891365
}))
13901366
}
@@ -1394,7 +1370,7 @@ export class Logger {
13941370
}
13951371

13961372
// Write directly to file instead of using this.info()
1397-
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}\n`
1373+
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}\n`.replace(this.ANSI_PATTERN, '')
13981374
await this.writeToFile(logEntry)
13991375
}
14001376

@@ -1412,7 +1388,7 @@ export class Logger {
14121388

14131389
return new Promise((resolve) => {
14141390
// Use console.error for display
1415-
console.error(`${terminalStyles.cyan('?')} ${message} (y/n) `)
1391+
console.error(`${styles.cyan('?')} ${message} (y/n) `)
14161392

14171393
const onData = (data: Buffer) => {
14181394
const input = data.toString().trim().toLowerCase()
@@ -1522,15 +1498,15 @@ export class Logger {
15221498
// Console output with fancy formatting
15231499
if (this.fancy && !isBrowserProcess()) {
15241500
// Make tag optional based on showTags property and use custom format
1525-
const tag = this.options.showTags !== false && this.name ? terminalStyles.gray(this.formatTag(this.name)) : ''
1526-
const spinnerChar = terminalStyles.blue('◐')
1527-
console.error(`${spinnerChar} ${tag} ${terminalStyles.cyan(formattedMessage)}`)
1501+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : ''
1502+
const spinnerChar = styles.blue('◐')
1503+
console.error(`${spinnerChar} ${tag} ${styles.cyan(formattedMessage)}`)
15281504
}
15291505

15301506
// Log to file directly instead of using this.info()
15311507
const timestamp = new Date()
15321508
const formattedDate = timestamp.toISOString()
1533-
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}\n`
1509+
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}\n`.replace(this.ANSI_PATTERN, '')
15341510

15351511
await this.writeToFile(logEntry)
15361512
}
@@ -1634,18 +1610,18 @@ export class Logger {
16341610
const filledLength = Math.round((barState.barLength * percent) / 100)
16351611
const emptyLength = barState.barLength - filledLength
16361612

1637-
const filledBar = terminalStyles.green('━'.repeat(filledLength))
1638-
const emptyBar = terminalStyles.gray('━'.repeat(emptyLength))
1613+
const filledBar = styles.green('━'.repeat(filledLength))
1614+
const emptyBar = styles.gray('━'.repeat(emptyLength))
16391615
const bar = `[${filledBar}${emptyBar}]`
16401616

16411617
const percentageText = `${percent}%`.padStart(4)
16421618
const messageText = barState.message ? ` ${barState.message}` : ''
16431619

16441620
// Use a simpler icon for progress
1645-
const icon = isFinished || percent === 100 ? terminalStyles.green('✓') : terminalStyles.blue('▶')
1621+
const icon = isFinished || percent === 100 ? styles.green('✓') : styles.blue('▶')
16461622

16471623
// Add tag if enabled
1648-
const tag = this.options.showTags !== false && this.name ? ` ${terminalStyles.gray(this.formatTag(this.name))}` : ''
1624+
const tag = this.options.showTags !== false && this.name ? ` ${styles.gray(this.formatTag(this.name))}` : ''
16491625

16501626
const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`
16511627

src/style.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export type TerminalStyle = (text: string) => string
2+
3+
export interface TerminalStyles {
4+
red: TerminalStyle
5+
green: TerminalStyle
6+
yellow: TerminalStyle
7+
blue: TerminalStyle
8+
magenta: TerminalStyle
9+
cyan: TerminalStyle
10+
white: TerminalStyle
11+
gray: TerminalStyle
12+
bgRed: TerminalStyle
13+
bgYellow: TerminalStyle
14+
bold: TerminalStyle
15+
dim: TerminalStyle
16+
italic: TerminalStyle
17+
underline: TerminalStyle
18+
reset: string
19+
}
20+
21+
const terminalStyles: TerminalStyles = {
22+
red: (text: string) => `\x1B[31m${text}\x1B[0m`,
23+
green: (text: string) => `\x1B[32m${text}\x1B[0m`,
24+
yellow: (text: string) => `\x1B[33m${text}\x1B[0m`,
25+
blue: (text: string) => `\x1B[34m${text}\x1B[0m`,
26+
magenta: (text: string) => `\x1B[35m${text}\x1B[0m`,
27+
cyan: (text: string) => `\x1B[36m${text}\x1B[0m`,
28+
white: (text: string) => `\x1B[37m${text}\x1B[0m`,
29+
gray: (text: string) => `\x1B[90m${text}\x1B[0m`,
30+
bgRed: (text: string) => `\x1B[41m${text}\x1B[0m`,
31+
bgYellow: (text: string) => `\x1B[43m${text}\x1B[0m`,
32+
bold: (text: string) => `\x1B[1m${text}\x1B[0m`,
33+
dim: (text: string) => `\x1B[2m${text}\x1B[0m`,
34+
italic: (text: string) => `\x1B[3m${text}\x1B[0m`,
35+
underline: (text: string) => `\x1B[4m${text}\x1B[0m`,
36+
reset: '\x1B[0m',
37+
}
38+
39+
export const styles: TerminalStyles = terminalStyles
40+
export const red: TerminalStyle = terminalStyles.red
41+
export const green: TerminalStyle = terminalStyles.green
42+
export const yellow: TerminalStyle = terminalStyles.yellow
43+
export const blue: TerminalStyle = terminalStyles.blue
44+
export const magenta: TerminalStyle = terminalStyles.magenta
45+
export const cyan: TerminalStyle = terminalStyles.cyan
46+
export const white: TerminalStyle = terminalStyles.white
47+
export const gray: TerminalStyle = terminalStyles.gray
48+
export const bgRed: TerminalStyle = terminalStyles.bgRed
49+
export const bgYellow: TerminalStyle = terminalStyles.bgYellow
50+
export const bold: TerminalStyle = terminalStyles.bold
51+
export const dim: TerminalStyle = terminalStyles.dim
52+
export const italic: TerminalStyle = terminalStyles.italic
53+
export const underline: TerminalStyle = terminalStyles.underline
54+
export const reset: string = terminalStyles.reset

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"moduleDetection": "force",
66
"module": "esnext",
77
"moduleResolution": "bundler",
8+
"paths": {
9+
"@stacksjs/clarity": ["./src/*"]
10+
},
811
"resolveJsonModule": true,
912
"types": ["bun"],
1013
"allowImportingTsExtensions": true,

0 commit comments

Comments
 (0)