@mks2508/better-logger 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +23 -1
- package/dist/Logger.d.ts +117 -4
- package/dist/Logger.d.ts.map +1 -1
- package/dist/ScopedLogger.d.ts +17 -0
- package/dist/ScopedLogger.d.ts.map +1 -1
- package/dist/chunks/{Logger-D-gmmgR2.js → Logger-BZ7B7OXE.js} +228 -175
- package/dist/chunks/Logger-BZ7B7OXE.js.map +1 -0
- package/dist/chunks/Logger-Cbi1f10o.js +2 -0
- package/dist/chunks/Logger-Cbi1f10o.js.map +1 -0
- package/dist/chunks/cli-module-CadqgZ1Z.js +2 -0
- package/dist/chunks/cli-module-CadqgZ1Z.js.map +1 -0
- package/dist/chunks/cli-module-ClIJmaT8.js +383 -0
- package/dist/chunks/cli-module-ClIJmaT8.js.map +1 -0
- package/dist/chunks/color-converter-CCSQRztd.js +2 -0
- package/dist/chunks/color-converter-CCSQRztd.js.map +1 -0
- package/dist/chunks/color-converter-_Xdmsy7E.js +462 -0
- package/dist/chunks/color-converter-_Xdmsy7E.js.map +1 -0
- package/dist/chunks/{environment-COWvu6Wz.js → environment-C1xxvc8l.js} +26 -476
- package/dist/chunks/environment-C1xxvc8l.js.map +1 -0
- package/dist/chunks/environment-Cprkw3g9.js +4 -0
- package/dist/chunks/environment-Cprkw3g9.js.map +1 -0
- package/dist/chunks/{formatting-CYjT9yhO.js → formatting-DMJxYq9o.js} +18 -18
- package/dist/chunks/{formatting-CYjT9yhO.js.map → formatting-DMJxYq9o.js.map} +1 -1
- package/dist/chunks/{formatting-Cg5YhB9Y.js → formatting-DpKxCsXq.js} +2 -2
- package/dist/chunks/{formatting-Cg5YhB9Y.js.map → formatting-DpKxCsXq.js.map} +1 -1
- package/dist/cli-module.d.ts +15 -0
- package/dist/cli-module.d.ts.map +1 -0
- package/dist/cli-primitives/box.d.ts +11 -0
- package/dist/cli-primitives/box.d.ts.map +1 -0
- package/dist/cli-primitives/cli-table.d.ts +11 -0
- package/dist/cli-primitives/cli-table.d.ts.map +1 -0
- package/dist/cli-primitives/divider.d.ts +11 -0
- package/dist/cli-primitives/divider.d.ts.map +1 -0
- package/dist/cli-primitives/header.d.ts +12 -0
- package/dist/cli-primitives/header.d.ts.map +1 -0
- package/dist/cli-primitives/index.d.ts +13 -0
- package/dist/cli-primitives/index.d.ts.map +1 -0
- package/dist/cli-primitives/server-fallback.d.ts +25 -0
- package/dist/cli-primitives/server-fallback.d.ts.map +1 -0
- package/dist/cli-primitives/spinner.d.ts +47 -0
- package/dist/cli-primitives/spinner.d.ts.map +1 -0
- package/dist/cli-primitives/step.d.ts +11 -0
- package/dist/cli-primitives/step.d.ts.map +1 -0
- package/dist/cli.cjs +2 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +9 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.js +2 -2
- package/dist/exports-module.d.ts +1 -1
- package/dist/exports-module.d.ts.map +1 -1
- package/dist/exports.cjs +1 -1
- package/dist/exports.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +71 -53
- package/dist/index.js.map +1 -1
- package/dist/styling-module.d.ts +1 -1
- package/dist/styling-module.d.ts.map +1 -1
- package/dist/styling.cjs +1 -1
- package/dist/styling.js +3 -3
- package/dist/types/core.d.ts +47 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +28 -3
- package/packages/core/package.json +1 -1
- package/playground/demo-all.ts +95 -0
- package/playground/demo-box.ts +85 -0
- package/playground/demo-levels.ts +56 -0
- package/playground/demo-real-world.ts +98 -0
- package/playground/demo-spinner.ts +77 -0
- package/playground/demo-steps.ts +72 -0
- package/playground/demo-table.ts +83 -0
- package/src/Logger.ts +213 -8
- package/src/ScopedLogger.ts +43 -1
- package/src/cli-module.ts +21 -0
- package/src/cli-primitives/box.ts +86 -0
- package/src/cli-primitives/cli-table.ts +62 -0
- package/src/cli-primitives/divider.ts +17 -0
- package/src/cli-primitives/header.ts +18 -0
- package/src/cli-primitives/index.ts +13 -0
- package/src/cli-primitives/server-fallback.ts +54 -0
- package/src/cli-primitives/spinner.ts +133 -0
- package/src/cli-primitives/step.ts +22 -0
- package/src/constants.ts +14 -2
- package/src/index.ts +18 -1
- package/src/types/core.ts +53 -0
- package/src/types/index.ts +4 -0
- package/vite.config.ts +33 -0
- package/dist/chunks/Logger-D-gmmgR2.js.map +0 -1
- package/dist/chunks/Logger-D7cfaz15.js +0 -2
- package/dist/chunks/Logger-D7cfaz15.js.map +0 -1
- package/dist/chunks/environment-COWvu6Wz.js.map +0 -1
- package/dist/chunks/environment-C_8J-zQ_.js +0 -4
- package/dist/chunks/environment-C_8J-zQ_.js.map +0 -1
package/src/ScopedLogger.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from './Logger.js';
|
|
2
|
-
import type { TimerEntry, Bindings } from './types/index.js';
|
|
2
|
+
import type { TimerEntry, Bindings, ISpinnerHandle, IBoxOptions, ITableOptions, CLILogLevel } from './types/index.js';
|
|
3
3
|
|
|
4
4
|
export class ScopedLogger {
|
|
5
5
|
private readonly parent: Logger;
|
|
@@ -105,6 +105,48 @@ export class ScopedLogger {
|
|
|
105
105
|
console.trace(`[${this.scopeName}]`);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// ===== CLI PRIMITIVES (v5.0 delegation) =====
|
|
109
|
+
|
|
110
|
+
/** @see Logger.step */
|
|
111
|
+
step(current: number, total: number, message: string): void {
|
|
112
|
+
this.parent.step(current, total, message);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @see Logger.header */
|
|
116
|
+
header(title: string, subtitle?: string): void {
|
|
117
|
+
this.parent.header(title, subtitle);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @see Logger.divider */
|
|
121
|
+
divider(): void {
|
|
122
|
+
this.parent.divider();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @see Logger.blank */
|
|
126
|
+
blank(): void {
|
|
127
|
+
this.parent.blank();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @see Logger.box */
|
|
131
|
+
box(content: string, options?: IBoxOptions): void {
|
|
132
|
+
this.parent.box(content, options);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @see Logger.cliTable */
|
|
136
|
+
cliTable(rows: Record<string, unknown>[], options?: ITableOptions): void {
|
|
137
|
+
this.parent.cliTable(rows, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @see Logger.spinner */
|
|
141
|
+
spinner(message: string): ISpinnerHandle {
|
|
142
|
+
return this.parent.spinner(message);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @see Logger.setCLILevel */
|
|
146
|
+
setCLILevel(level: CLILogLevel): void {
|
|
147
|
+
this.parent.setCLILevel(level);
|
|
148
|
+
}
|
|
149
|
+
|
|
108
150
|
_pushContext(context: string): void {
|
|
109
151
|
this.contextStack.push(context);
|
|
110
152
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI-only entry point for @mks2508/better-logger/cli
|
|
3
|
+
* Exports only CLI primitives for minimal bundle size in CLI tools.
|
|
4
|
+
* @version 5.0.0
|
|
5
|
+
* @since 5.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { renderStep } from './cli-primitives/step.js';
|
|
9
|
+
export { renderHeader } from './cli-primitives/header.js';
|
|
10
|
+
export { renderDivider } from './cli-primitives/divider.js';
|
|
11
|
+
export { renderBox } from './cli-primitives/box.js';
|
|
12
|
+
export { renderTable } from './cli-primitives/cli-table.js';
|
|
13
|
+
export { SpinnerManager, NoopSpinner } from './cli-primitives/spinner.js';
|
|
14
|
+
export { ServerFallback } from './cli-primitives/server-fallback.js';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
CLILogLevel,
|
|
18
|
+
ISpinnerHandle,
|
|
19
|
+
IBoxOptions,
|
|
20
|
+
ITableOptions,
|
|
21
|
+
} from './types/index.js';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Box renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IBoxOptions } from '../types/core.js';
|
|
7
|
+
import type { ColorCapability } from '../terminal/color-converter.js';
|
|
8
|
+
import { getANSIForeground, ANSI } from '../terminal/color-converter.js';
|
|
9
|
+
import { getTerminalWidth } from '../utils/environment-detector.js';
|
|
10
|
+
import { stripAnsi, getVisibleLength } from '../terminal/formatter.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Border character sets for different styles
|
|
14
|
+
*/
|
|
15
|
+
const BORDER_CHARS = {
|
|
16
|
+
single: { tl: '\u250c', tr: '\u2510', bl: '\u2514', br: '\u2518', h: '\u2500', v: '\u2502' },
|
|
17
|
+
rounded: { tl: '\u256d', tr: '\u256e', bl: '\u2570', br: '\u256f', h: '\u2500', v: '\u2502' },
|
|
18
|
+
double: { tl: '\u2554', tr: '\u2557', bl: '\u255a', br: '\u255d', h: '\u2550', v: '\u2551' },
|
|
19
|
+
bold: { tl: '\u250f', tr: '\u2513', bl: '\u2517', br: '\u251b', h: '\u2501', v: '\u2503' },
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Renders content inside a bordered box
|
|
24
|
+
* @param content - Content string (may contain newlines)
|
|
25
|
+
* @param options - Box rendering options
|
|
26
|
+
* @param colorCap - Terminal color capability
|
|
27
|
+
* @returns Formatted box string with border
|
|
28
|
+
*/
|
|
29
|
+
export function renderBox(content: string, options: IBoxOptions = {}, colorCap: ColorCapability = 'full'): string {
|
|
30
|
+
const {
|
|
31
|
+
title,
|
|
32
|
+
borderColor,
|
|
33
|
+
borderStyle = 'rounded',
|
|
34
|
+
padding = 0,
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
const chars = BORDER_CHARS[borderStyle] ?? BORDER_CHARS.rounded;
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
const maxTermWidth = Math.min(getTerminalWidth() - 4, 80);
|
|
40
|
+
|
|
41
|
+
// Calculate content width from visible text
|
|
42
|
+
const contentWidths = lines.map(l => getVisibleLength(l));
|
|
43
|
+
const titleWidth = title ? stripAnsi(title).length + 2 : 0; // +2 for spaces around title
|
|
44
|
+
const maxContentWidth = Math.max(...contentWidths, titleWidth);
|
|
45
|
+
const innerWidth = Math.min(maxContentWidth + 2, maxTermWidth); // +2 for horizontal padding
|
|
46
|
+
|
|
47
|
+
// Color wrapper for border chars
|
|
48
|
+
const bc = borderColor && colorCap !== 'none'
|
|
49
|
+
? getANSIForeground(borderColor, colorCap)
|
|
50
|
+
: '';
|
|
51
|
+
const reset = bc ? ANSI.reset : '';
|
|
52
|
+
|
|
53
|
+
const wrap = (char: string) => `${bc}${char}${reset}`;
|
|
54
|
+
|
|
55
|
+
// Build top border (with optional title)
|
|
56
|
+
let topBorder: string;
|
|
57
|
+
if (title) {
|
|
58
|
+
const titleStr = ` ${title} `;
|
|
59
|
+
const afterTitle = innerWidth - stripAnsi(titleStr).length;
|
|
60
|
+
topBorder = ` ${wrap(chars.tl)}${wrap(chars.h)}${wrap(titleStr)}${wrap(chars.h.repeat(Math.max(0, afterTitle - 1)))}${wrap(chars.tr)}`;
|
|
61
|
+
} else {
|
|
62
|
+
topBorder = ` ${wrap(chars.tl)}${wrap(chars.h.repeat(innerWidth))}${wrap(chars.tr)}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Build bottom border
|
|
66
|
+
const bottomBorder = ` ${wrap(chars.bl)}${wrap(chars.h.repeat(innerWidth))}${wrap(chars.br)}`;
|
|
67
|
+
|
|
68
|
+
// Build padding lines
|
|
69
|
+
const emptyLine = ` ${wrap(chars.v)}${' '.repeat(innerWidth)}${wrap(chars.v)}`;
|
|
70
|
+
const paddingLines = padding > 0 ? Array(padding).fill(emptyLine) : [];
|
|
71
|
+
|
|
72
|
+
// Build content lines
|
|
73
|
+
const contentLines = lines.map(line => {
|
|
74
|
+
const visible = getVisibleLength(line);
|
|
75
|
+
const pad = innerWidth - visible - 1; // -1 for left space
|
|
76
|
+
return ` ${wrap(chars.v)} ${line}${' '.repeat(Math.max(0, pad))}${wrap(chars.v)}`;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return [
|
|
80
|
+
topBorder,
|
|
81
|
+
...paddingLines,
|
|
82
|
+
...contentLines,
|
|
83
|
+
...paddingLines,
|
|
84
|
+
bottomBorder,
|
|
85
|
+
].join('\n');
|
|
86
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI table renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ITableOptions } from '../types/core.js';
|
|
7
|
+
import type { ColorCapability } from '../terminal/color-converter.js';
|
|
8
|
+
import { getANSIForeground, ANSI } from '../terminal/color-converter.js';
|
|
9
|
+
import { getVisibleLength, padToWidth } from '../terminal/formatter.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders an array of objects as a formatted ASCII table
|
|
13
|
+
* @param rows - Array of row objects
|
|
14
|
+
* @param options - Table rendering options
|
|
15
|
+
* @param colorCap - Terminal color capability
|
|
16
|
+
* @returns Formatted table string
|
|
17
|
+
*/
|
|
18
|
+
export function renderTable(
|
|
19
|
+
rows: Record<string, unknown>[],
|
|
20
|
+
options: ITableOptions = {},
|
|
21
|
+
colorCap: ColorCapability = 'full'
|
|
22
|
+
): string {
|
|
23
|
+
if (rows.length === 0) return '';
|
|
24
|
+
|
|
25
|
+
// Determine columns from options or first row's keys
|
|
26
|
+
const columns = options.columns ?? Object.keys(rows[0]!);
|
|
27
|
+
const headers = options.head ?? columns;
|
|
28
|
+
|
|
29
|
+
// Calculate column widths (max of header and all values)
|
|
30
|
+
const colWidths = columns.map((col, i) => {
|
|
31
|
+
const headerLen = getVisibleLength(headers[i] ?? col);
|
|
32
|
+
const maxValueLen = rows.reduce((max, row) => {
|
|
33
|
+
const val = String(row[col] ?? '');
|
|
34
|
+
return Math.max(max, getVisibleLength(val));
|
|
35
|
+
}, 0);
|
|
36
|
+
return Math.max(headerLen, maxValueLen) + 2; // +2 for padding
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Color helpers
|
|
40
|
+
const cyan = colorCap !== 'none' ? getANSIForeground('#00bcd4', colorCap) : '';
|
|
41
|
+
const dim = colorCap !== 'none' ? ANSI.dim : '';
|
|
42
|
+
const reset = colorCap !== 'none' ? ANSI.reset : '';
|
|
43
|
+
|
|
44
|
+
// Build header row
|
|
45
|
+
const headerRow = ' ' + columns.map((col, i) => {
|
|
46
|
+
const label = headers[i] ?? col;
|
|
47
|
+
return cyan + ANSI.bold + padToWidth(` ${label}`, colWidths[i]!) + reset;
|
|
48
|
+
}).join('');
|
|
49
|
+
|
|
50
|
+
// Build separator
|
|
51
|
+
const separator = ' ' + dim + colWidths.map(w => '\u2500'.repeat(w)).join('\u2500') + reset;
|
|
52
|
+
|
|
53
|
+
// Build data rows
|
|
54
|
+
const dataRows = rows.map(row => {
|
|
55
|
+
return ' ' + columns.map((col, i) => {
|
|
56
|
+
const val = String(row[col] ?? '');
|
|
57
|
+
return padToWidth(` ${val}`, colWidths[i]!);
|
|
58
|
+
}).join('');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return [headerRow, separator, ...dataRows].join('\n');
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Divider renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ANSI } from '../terminal/color-converter.js';
|
|
7
|
+
import { getTerminalWidth } from '../utils/environment-detector.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders a horizontal divider line
|
|
11
|
+
* @param width - Optional explicit width (defaults to terminal width capped at 60)
|
|
12
|
+
* @returns Formatted divider string
|
|
13
|
+
*/
|
|
14
|
+
export function renderDivider(width?: number): string {
|
|
15
|
+
const w = width ?? Math.min(getTerminalWidth() - 4, 60);
|
|
16
|
+
return ANSI.dim + ' ' + '\u2500'.repeat(w) + ANSI.reset;
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Header renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ANSI } from '../terminal/color-converter.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders a styled header with optional subtitle
|
|
10
|
+
* @param title - Main title text
|
|
11
|
+
* @param subtitle - Optional subtitle (rendered dimmed)
|
|
12
|
+
* @returns Formatted header string
|
|
13
|
+
*/
|
|
14
|
+
export function renderHeader(title: string, subtitle?: string): string {
|
|
15
|
+
const t = ANSI.bold + title + ANSI.reset;
|
|
16
|
+
const s = subtitle ? ANSI.dim + ` ${subtitle}` + ANSI.reset : '';
|
|
17
|
+
return ` ${t}${s}`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview CLI Primitives - Built-in terminal UI components for better-logger
|
|
3
|
+
* @version 5.0.0
|
|
4
|
+
* @since 5.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { renderStep } from './step.js';
|
|
8
|
+
export { renderHeader } from './header.js';
|
|
9
|
+
export { renderDivider } from './divider.js';
|
|
10
|
+
export { renderBox } from './box.js';
|
|
11
|
+
export { renderTable } from './cli-table.js';
|
|
12
|
+
export { SpinnerManager, NoopSpinner } from './spinner.js';
|
|
13
|
+
export { ServerFallback } from './server-fallback.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server/JSON fallback for CLI primitives in non-TTY environments
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IBoxOptions, ITableOptions } from '../types/core.js';
|
|
7
|
+
import type { Logger } from '../Logger.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Server-mode fallback: outputs CLI primitives as plain logger calls
|
|
11
|
+
* when not running in an interactive terminal.
|
|
12
|
+
*
|
|
13
|
+
* @since 5.0.0
|
|
14
|
+
*/
|
|
15
|
+
export class ServerFallback {
|
|
16
|
+
private logger: Logger;
|
|
17
|
+
|
|
18
|
+
constructor(logger: Logger) {
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Render step as plain info log */
|
|
23
|
+
step(current: number, total: number, msg: string): void {
|
|
24
|
+
this.logger.info(`[${current}/${total}] ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Render header as plain info log */
|
|
28
|
+
header(title: string, subtitle?: string): void {
|
|
29
|
+
const text = subtitle ? `${title} ${subtitle}` : title;
|
|
30
|
+
this.logger.info(text);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Divider is a no-op in server mode */
|
|
34
|
+
divider(): void {
|
|
35
|
+
// No-op in server/JSON mode
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Blank line is a no-op in server mode */
|
|
39
|
+
blank(): void {
|
|
40
|
+
// No-op in server/JSON mode
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Render box content as plain info log */
|
|
44
|
+
box(content: string, _options?: IBoxOptions): void {
|
|
45
|
+
this.logger.info(content);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Render table rows as plain info logs */
|
|
49
|
+
cliTable(rows: Record<string, unknown>[], _options?: ITableOptions): void {
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
this.logger.info(JSON.stringify(row));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Built-in spinner implementation for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ISpinnerHandle, LoggerConfig } from '../types/core.js';
|
|
7
|
+
import type { Logger } from '../Logger.js';
|
|
8
|
+
import { ANSI } from '../terminal/color-converter.js';
|
|
9
|
+
|
|
10
|
+
/** Braille-based spinner animation frames */
|
|
11
|
+
const SPINNER_FRAMES = ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'];
|
|
12
|
+
|
|
13
|
+
/** Spinner frame interval in milliseconds */
|
|
14
|
+
const FRAME_INTERVAL = 80;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Interactive terminal spinner with in-place line updates.
|
|
18
|
+
* Writes to stderr to avoid polluting piped stdout.
|
|
19
|
+
*
|
|
20
|
+
* @implements {ISpinnerHandle}
|
|
21
|
+
* @since 5.0.0
|
|
22
|
+
*/
|
|
23
|
+
export class SpinnerManager implements ISpinnerHandle {
|
|
24
|
+
private message: string;
|
|
25
|
+
private frameIndex = 0;
|
|
26
|
+
private interval: ReturnType<typeof setInterval> | null = null;
|
|
27
|
+
private _logger: Logger;
|
|
28
|
+
|
|
29
|
+
constructor(message: string, _config: LoggerConfig, logger: Logger) {
|
|
30
|
+
this.message = message;
|
|
31
|
+
this._logger = logger;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Start the spinner animation */
|
|
35
|
+
start(): void {
|
|
36
|
+
if (this.interval) return; // Already running
|
|
37
|
+
|
|
38
|
+
// Switch logger to buffer mode to prevent garbled output
|
|
39
|
+
this._logger.updateConfig({ outputMode: 'silent' });
|
|
40
|
+
|
|
41
|
+
this.frameIndex = 0;
|
|
42
|
+
this.render();
|
|
43
|
+
this.interval = setInterval(() => {
|
|
44
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
45
|
+
this.render();
|
|
46
|
+
}, FRAME_INTERVAL);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Stop the spinner without a status message */
|
|
50
|
+
stop(): void {
|
|
51
|
+
this.clearInterval();
|
|
52
|
+
this.clearLine();
|
|
53
|
+
// Restore logger output
|
|
54
|
+
this._logger.updateConfig({ outputMode: 'console' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Stop with a success indicator */
|
|
58
|
+
succeed(msg?: string): void {
|
|
59
|
+
this.clearInterval();
|
|
60
|
+
this.clearLine();
|
|
61
|
+
const text = msg ?? this.message;
|
|
62
|
+
process.stderr.write(` ${ANSI.fg.green}\u2713${ANSI.reset} ${text}\n`);
|
|
63
|
+
this._logger.updateConfig({ outputMode: 'console' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Stop with a failure indicator */
|
|
67
|
+
fail(msg?: string): void {
|
|
68
|
+
this.clearInterval();
|
|
69
|
+
this.clearLine();
|
|
70
|
+
const text = msg ?? this.message;
|
|
71
|
+
process.stderr.write(` ${ANSI.fg.red}\u2717${ANSI.reset} ${text}\n`);
|
|
72
|
+
this._logger.updateConfig({ outputMode: 'console' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Update the spinner text while running */
|
|
76
|
+
text(msg: string): void {
|
|
77
|
+
this.message = msg;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private render(): void {
|
|
81
|
+
const frame = SPINNER_FRAMES[this.frameIndex] ?? SPINNER_FRAMES[0];
|
|
82
|
+
const line = ` ${ANSI.fg.cyan}${frame}${ANSI.reset} ${this.message}`;
|
|
83
|
+
process.stderr.write(`\r\x1b[K${line}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private clearLine(): void {
|
|
87
|
+
process.stderr.write('\r\x1b[K');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private clearInterval(): void {
|
|
91
|
+
if (this.interval) {
|
|
92
|
+
clearInterval(this.interval);
|
|
93
|
+
this.interval = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* No-op spinner for non-TTY environments or silent mode.
|
|
100
|
+
* Logs start/succeed/fail as plain log messages.
|
|
101
|
+
*
|
|
102
|
+
* @implements {ISpinnerHandle}
|
|
103
|
+
* @since 5.0.0
|
|
104
|
+
*/
|
|
105
|
+
export class NoopSpinner implements ISpinnerHandle {
|
|
106
|
+
private message: string;
|
|
107
|
+
private _logger: Logger;
|
|
108
|
+
|
|
109
|
+
constructor(message: string, logger: Logger) {
|
|
110
|
+
this.message = message;
|
|
111
|
+
this._logger = logger;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
start(): void {
|
|
115
|
+
this._logger.info(this.message);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
stop(): void {
|
|
119
|
+
// No-op
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
succeed(msg?: string): void {
|
|
123
|
+
this._logger.success(msg ?? this.message);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fail(msg?: string): void {
|
|
127
|
+
this._logger.error(msg ?? this.message);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
text(msg: string): void {
|
|
131
|
+
this.message = msg;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Step progress renderer for CLI primitives
|
|
3
|
+
* @since 5.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getANSIForeground, ANSI, type ColorCapability } from '../terminal/color-converter.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders a step progress indicator like ` [2/5] Analyzing repository...`
|
|
10
|
+
* @param current - Current step number
|
|
11
|
+
* @param total - Total number of steps
|
|
12
|
+
* @param msg - Step description message
|
|
13
|
+
* @param colorCap - Terminal color capability
|
|
14
|
+
* @returns Formatted step string
|
|
15
|
+
*/
|
|
16
|
+
export function renderStep(current: number, total: number, msg: string, colorCap: ColorCapability): string {
|
|
17
|
+
const label = `[${current}/${total}]`;
|
|
18
|
+
const colored = colorCap !== 'none'
|
|
19
|
+
? getANSIForeground('#00bcd4', colorCap) + ANSI.bold + label + ANSI.reset
|
|
20
|
+
: label;
|
|
21
|
+
return ` ${colored} ${msg}`;
|
|
22
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Global constants for Advanced Logger
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { LogLevel, AdaptiveColors } from './types/index.js';
|
|
5
|
+
import type { LogLevel, AdaptiveColors, CLILogLevel, Verbosity } from './types/index.js';
|
|
6
6
|
import type { LevelStyleConfig } from './utils/index.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -298,4 +298,16 @@ export function getOptimalConfig() {
|
|
|
298
298
|
...DEFAULT_CONFIG,
|
|
299
299
|
...BUILD_PRESETS[preset]
|
|
300
300
|
};
|
|
301
|
-
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* CLI log level to internal verbosity mapping
|
|
305
|
+
* @since 5.0.0
|
|
306
|
+
*/
|
|
307
|
+
export const CLI_LEVEL_MAP: Record<CLILogLevel, { verbosity: Verbosity; showPrimitives: boolean }> = {
|
|
308
|
+
silent: { verbosity: 'silent', showPrimitives: false },
|
|
309
|
+
quiet: { verbosity: 'error', showPrimitives: false },
|
|
310
|
+
normal: { verbosity: 'info', showPrimitives: true },
|
|
311
|
+
verbose: { verbosity: 'debug', showPrimitives: true },
|
|
312
|
+
debug: { verbosity: 'debug', showPrimitives: true },
|
|
313
|
+
} as const;
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,10 @@ import type {
|
|
|
18
18
|
ThemeVariant,
|
|
19
19
|
BannerType,
|
|
20
20
|
StyleOptions,
|
|
21
|
-
ILogHandler
|
|
21
|
+
ILogHandler,
|
|
22
|
+
CLILogLevel,
|
|
23
|
+
IBoxOptions,
|
|
24
|
+
ITableOptions,
|
|
22
25
|
} from './types/index.js';
|
|
23
26
|
|
|
24
27
|
// Lazy singleton - se inicializa solo cuando se necesita
|
|
@@ -115,6 +118,16 @@ export const logAnimated = (message: string, duration?: number) =>
|
|
|
115
118
|
getLogger().logAnimated(message, duration);
|
|
116
119
|
export const cli = (command: string) => getLogger().cli(command);
|
|
117
120
|
|
|
121
|
+
// CLI Primitives (v5.0)
|
|
122
|
+
export const step = (current: number, total: number, message: string) => getLogger().step(current, total, message);
|
|
123
|
+
export const spinner = (message: string) => getLogger().spinner(message);
|
|
124
|
+
export const box = (content: string, options?: IBoxOptions) => getLogger().box(content, options);
|
|
125
|
+
export const cliTable = (rows: Record<string, unknown>[], options?: ITableOptions) => getLogger().cliTable(rows, options);
|
|
126
|
+
export const header = (title: string, subtitle?: string) => getLogger().header(title, subtitle);
|
|
127
|
+
export const divider = () => getLogger().divider();
|
|
128
|
+
export const blank = () => getLogger().blank();
|
|
129
|
+
export const setCLILevel = (level: CLILogLevel) => getLogger().setCLILevel(level);
|
|
130
|
+
|
|
118
131
|
// Type exports
|
|
119
132
|
export type {
|
|
120
133
|
LogLevel,
|
|
@@ -152,6 +165,10 @@ export type {
|
|
|
152
165
|
ColumnAlign,
|
|
153
166
|
ColumnConfig,
|
|
154
167
|
LogOptions,
|
|
168
|
+
CLILogLevel,
|
|
169
|
+
ISpinnerHandle,
|
|
170
|
+
IBoxOptions,
|
|
171
|
+
ITableOptions,
|
|
155
172
|
} from './types/index.js';
|
|
156
173
|
|
|
157
174
|
// Styling utilities
|
package/src/types/core.ts
CHANGED
|
@@ -176,6 +176,8 @@ export interface LoggerConfig {
|
|
|
176
176
|
outputMode?: OutputMode;
|
|
177
177
|
/** Custom writer when outputMode is 'custom' @since 4.0.0 */
|
|
178
178
|
outputWriter?: OutputWriter;
|
|
179
|
+
/** CLI verbosity level for controlling primitive output @since 5.0.0 */
|
|
180
|
+
cliLevel?: CLILogLevel;
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
/**
|
|
@@ -361,4 +363,55 @@ export interface LogOptions {
|
|
|
361
363
|
keyValue?: boolean;
|
|
362
364
|
badgeStyle?: BadgeStyle;
|
|
363
365
|
timestampFormat?: TimestampFormat;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ===== CLI PRIMITIVES (v5.0) =====
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* CLI verbosity levels for controlling primitive output
|
|
372
|
+
* @since 5.0.0
|
|
373
|
+
*/
|
|
374
|
+
export type CLILogLevel = 'silent' | 'quiet' | 'normal' | 'verbose' | 'debug';
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Handle returned by logger.spinner() for controlling spinner lifecycle
|
|
378
|
+
* @since 5.0.0
|
|
379
|
+
*/
|
|
380
|
+
export interface ISpinnerHandle {
|
|
381
|
+
/** Start the spinner animation */
|
|
382
|
+
start(): void;
|
|
383
|
+
/** Stop the spinner without a status message */
|
|
384
|
+
stop(): void;
|
|
385
|
+
/** Stop the spinner with a success message */
|
|
386
|
+
succeed(msg?: string): void;
|
|
387
|
+
/** Stop the spinner with a failure message */
|
|
388
|
+
fail(msg?: string): void;
|
|
389
|
+
/** Update the spinner text while running */
|
|
390
|
+
text(msg: string): void;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Options for logger.box() output
|
|
395
|
+
* @since 5.0.0
|
|
396
|
+
*/
|
|
397
|
+
export interface IBoxOptions {
|
|
398
|
+
/** Title displayed in the top border */
|
|
399
|
+
title?: string;
|
|
400
|
+
/** Color of the border (hex, CSS name, or ANSI name) */
|
|
401
|
+
borderColor?: string;
|
|
402
|
+
/** Border character style */
|
|
403
|
+
borderStyle?: 'single' | 'double' | 'rounded' | 'bold';
|
|
404
|
+
/** Inner padding lines (default: 0) */
|
|
405
|
+
padding?: number;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Options for logger.cliTable() output
|
|
410
|
+
* @since 5.0.0
|
|
411
|
+
*/
|
|
412
|
+
export interface ITableOptions {
|
|
413
|
+
/** Column names to display (overrides auto-detection) */
|
|
414
|
+
columns?: string[];
|
|
415
|
+
/** Header labels (defaults to column names) */
|
|
416
|
+
head?: string[];
|
|
364
417
|
}
|
package/src/types/index.ts
CHANGED
package/vite.config.ts
CHANGED
|
@@ -57,6 +57,7 @@ const buildConfigs = {
|
|
|
57
57
|
entry: {
|
|
58
58
|
index: resolve(__dirname, 'src/index.ts'),
|
|
59
59
|
core: resolve(__dirname, 'src/core.ts'),
|
|
60
|
+
cli: resolve(__dirname, 'src/cli-module.ts'),
|
|
60
61
|
styling: resolve(__dirname, 'src/styling-module.ts'),
|
|
61
62
|
exports: resolve(__dirname, 'src/exports-module.ts')
|
|
62
63
|
},
|
|
@@ -210,6 +211,38 @@ const buildConfigs = {
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
// Build modular - Solo CLI primitives (spinners, boxes, tables, steps)
|
|
217
|
+
cli: {
|
|
218
|
+
...baseConfig,
|
|
219
|
+
plugins: [
|
|
220
|
+
dts({
|
|
221
|
+
...baseConfig.plugins[0].options,
|
|
222
|
+
outDir: 'packages/cli/dist/types',
|
|
223
|
+
include: ['src/cli-module.ts', 'src/cli-primitives/**/*', 'src/types/**/*', 'src/terminal/**/*', 'src/utils/environment-detector.ts'],
|
|
224
|
+
})
|
|
225
|
+
],
|
|
226
|
+
build: {
|
|
227
|
+
...baseConfig.build,
|
|
228
|
+
outDir: 'packages/cli/dist',
|
|
229
|
+
lib: {
|
|
230
|
+
entry: resolve(__dirname, 'src/cli-module.ts'),
|
|
231
|
+
name: 'BetterLoggerCLI',
|
|
232
|
+
formats: ['es', 'cjs'],
|
|
233
|
+
fileName: (format) => {
|
|
234
|
+
const ext = format === 'cjs' ? 'cjs' : 'js';
|
|
235
|
+
return `index.${ext}`;
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
rollupOptions: {
|
|
239
|
+
external: [],
|
|
240
|
+
output: {
|
|
241
|
+
globals: {},
|
|
242
|
+
exports: 'named'
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
213
246
|
}
|
|
214
247
|
};
|
|
215
248
|
|