-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlog.ts
More file actions
184 lines (160 loc) · 5.32 KB
/
log.ts
File metadata and controls
184 lines (160 loc) · 5.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/**
* Factory for creating a {@link Log} instance backed by `@clack/prompts`.
*
* @module
*/
import type { Writable } from 'node:stream'
import * as clack from '@clack/prompts'
import type { ClackBase } from '@/context/resolve-defaults.js'
import { mergeClackOpts, resolveClackBase } from '@/context/resolve-defaults.js'
import type {
BoxOptions,
DisplayConfig,
Log,
LogMessageOptions,
NoteOptions,
StreamLog,
} from '@/context/types.js'
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Per-call defaults for clack log methods.
*/
export interface LogDefaults {
/** Show navigation guide hints. */
readonly guide?: boolean
/** Custom output stream. */
readonly output?: Writable
}
/**
* Options for {@link createLog}.
*/
export interface CreateLogOptions {
/**
* Writable stream for raw output. Defaults to `process.stderr`.
*/
readonly output?: NodeJS.WritableStream
/** Per-call defaults merged into every clack log call. Method-level options win. */
readonly defaults?: LogDefaults
/** Box display defaults from {@link DisplayConfig}. */
readonly boxDefaults?: DisplayConfig['box']
}
/**
* Create a {@link Log} instance that delegates to `@clack/prompts` for
* structured terminal output.
*
* When `defaults` are provided, they are spread as the base of every clack
* call. Method-level options always take precedence.
*
* @param options - Optional configuration for the log instance.
* @returns A frozen Log object.
*/
export function createLog(options?: CreateLogOptions): Log {
const output = resolveOutput(options)
const base = resolveClackBase(options?.defaults)
const boxBase = resolveBoxBase(base, options?.boxDefaults)
return Object.freeze({
error(message: string, opts?: LogMessageOptions): void {
clack.log.error(message, mergeClackOpts(base, opts))
},
info(message: string, opts?: LogMessageOptions): void {
clack.log.info(message, mergeClackOpts(base, opts))
},
intro(title?: string): void {
clack.intro(title, base)
},
message(message: string, opts?: LogMessageOptions): void {
clack.log.message(message, mergeClackOpts(base, opts))
},
newline(): void {
output.write('\n')
},
note(message?: string, title?: string, opts?: NoteOptions): void {
clack.note(message, title, mergeClackOpts(base, opts))
},
box(message: string, title?: string, opts?: BoxOptions): void {
clack.box(message, title, mergeClackOpts(boxBase, opts))
},
outro(message?: string): void {
clack.outro(message, base)
},
raw(text: string): void {
output.write(`${text}\n`)
},
step(message: string, opts?: LogMessageOptions): void {
clack.log.step(message, mergeClackOpts(base, opts))
},
success(message: string, opts?: LogMessageOptions): void {
clack.log.success(message, mergeClackOpts(base, opts))
},
warn(message: string, opts?: LogMessageOptions): void {
clack.log.warn(message, mergeClackOpts(base, opts))
},
stream: createStreamLog(),
}) satisfies Log
}
// ---------------------------------------------------------------------------
// Private helpers
// ---------------------------------------------------------------------------
/**
* Resolve the output stream from options, defaulting to `process.stderr`.
*
* @private
* @param options - Optional configuration containing an output stream.
* @returns The resolved writable stream.
*/
function resolveOutput(options: CreateLogOptions | undefined): NodeJS.WritableStream {
if (options !== undefined && options.output !== undefined) {
return options.output
}
return process.stderr
}
/**
* Resolve the base options for box calls by merging common defaults with box-specific defaults.
*
* @private
* @param base - Common clack defaults (withGuide, output).
* @param boxDefaults - Box-specific display config defaults.
* @returns A merged object suitable for spreading into `clack.box()` calls.
*/
function resolveBoxBase(
base: ClackBase,
boxDefaults: DisplayConfig['box'] | undefined
): ClackBase & Partial<NonNullable<DisplayConfig['box']>> {
if (boxDefaults === undefined) {
return base
}
return { ...base, ...boxDefaults }
}
/**
* Create the streaming log methods that delegate to `@clack/prompts` stream API.
*
* Note: clack's stream methods (except `message`) do not accept per-call options,
* so display defaults are not passed here.
*
* @private
* @returns A frozen StreamLog object.
*/
function createStreamLog(): StreamLog {
return Object.freeze({
async info(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.info(iterable)
},
async success(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.success(iterable)
},
async error(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.error(iterable)
},
async warn(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.warn(iterable)
},
async step(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.step(iterable)
},
async message(iterable: AsyncIterable<string>): Promise<void> {
await clack.stream.message(iterable)
},
}) satisfies StreamLog
}