Skip to content

Commit 84d0257

Browse files
committed
refactor: use core settings directly in spinner implementation for consistent message handling
1 parent 37f6046 commit 84d0257

File tree

5 files changed

+75
-97
lines changed

5 files changed

+75
-97
lines changed

.changeset/five-chairs-poke.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
---
22
"@clack/prompts": minor
3+
"@clack/core": minor
34
---
45

56
Add support for customizable spinner cancel and error messages. Users can now customize these messages either per spinner instance or globally via the `updateSettings` function to support multilingual CLIs.
67

8+
This update also improves the architecture by exposing the core settings to the prompts package, enabling more consistent default message handling across the codebase.
9+
710
```ts
811
// Per-instance customization
912
const spinner = prompts.spinner({
1013
cancelMessage: 'Operación cancelada', // "Operation cancelled" in Spanish
1114
errorMessage: 'Se produjo un error' // "An error occurred" in Spanish
1215
});
1316

14-
// Global customization via settings
17+
// Global customization via updateSettings
1518
prompts.updateSettings({
1619
messages: {
1720
cancel: 'Operación cancelada', // "Operation cancelled" in Spanish
1821
error: 'Se produjo un error' // "An error occurred" in Spanish
1922
}
2023
});
2124

25+
// Settings can now be accessed directly
26+
console.log(prompts.settings.messages.cancel); // "Operación cancelada"
27+
2228
// Direct options take priority over global settings
2329
const spinner = prompts.spinner({
2430
cancelMessage: 'Cancelled', // This will be used instead of the global setting

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ export { default as SelectPrompt } from './prompts/select.js';
1010
export { default as SelectKeyPrompt } from './prompts/select-key.js';
1111
export { default as TextPrompt } from './prompts/text.js';
1212
export { block, isCancel } from './utils/index.js';
13-
export { updateSettings } from './utils/settings.js';
13+
export { updateSettings, settings } from './utils/settings.js';

packages/core/src/utils/settings.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ export type Action = (typeof actions)[number];
55
interface InternalClackSettings {
66
actions: Set<Action>;
77
aliases: Map<string, Action>;
8+
messages: {
9+
cancel: string;
10+
error: string;
11+
};
812
}
913

1014
export const settings: InternalClackSettings = {
@@ -19,6 +23,10 @@ export const settings: InternalClackSettings = {
1923
// opinionated defaults!
2024
['escape', 'cancel'],
2125
]),
26+
messages: {
27+
cancel: 'Canceled',
28+
error: 'Something went wrong',
29+
},
2230
};
2331

2432
export interface ClackSettings {
@@ -29,7 +37,23 @@ export interface ClackSettings {
2937
* @param aliases - An object that maps aliases to actions
3038
* @default { k: 'up', j: 'down', h: 'left', l: 'right', '\x03': 'cancel', 'escape': 'cancel' }
3139
*/
32-
aliases: Record<string, Action>;
40+
aliases?: Record<string, Action>;
41+
42+
/**
43+
* Custom messages for prompts
44+
*/
45+
messages?: {
46+
/**
47+
* Custom message to display when a spinner is cancelled
48+
* @default "Canceled"
49+
*/
50+
cancel?: string;
51+
/**
52+
* Custom message to display when a spinner encounters an error
53+
* @default "Something went wrong"
54+
*/
55+
error?: string;
56+
};
3357
}
3458

3559
export function updateSettings(updates: ClackSettings) {
@@ -40,14 +64,25 @@ export function updateSettings(updates: ClackSettings) {
4064

4165
switch (key) {
4266
case 'aliases': {
43-
for (const alias in value) {
44-
if (!Object.hasOwn(value, alias)) continue;
67+
const aliases = value as Record<string, Action>;
68+
for (const alias in aliases) {
69+
if (!Object.hasOwn(aliases, alias)) continue;
4570
if (!settings.aliases.has(alias)) {
46-
settings.aliases.set(alias, value[alias]);
71+
settings.aliases.set(alias, aliases[alias]);
4772
}
4873
}
4974
break;
5075
}
76+
case 'messages': {
77+
const messages = value as ClackSettings['messages'];
78+
if (messages?.cancel) {
79+
settings.messages.cancel = messages.cancel;
80+
}
81+
if (messages?.error) {
82+
settings.messages.error = messages.error;
83+
}
84+
break;
85+
}
5186
}
5287
}
5388
}

packages/prompts/src/index.test.ts

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,10 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
239239
});
240240

241241
test('uses global custom cancel message from settings', () => {
242-
prompts.updateSettings({
243-
messages: {
244-
cancel: 'Global cancel message',
245-
},
246-
});
242+
// Store original message
243+
const originalCancelMessage = prompts.settings.messages.cancel;
244+
// Set custom message
245+
prompts.settings.messages.cancel = 'Global cancel message';
247246

248247
const result = prompts.spinner({ output });
249248
result.start('Test operation');
@@ -252,20 +251,15 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
252251

253252
expect(output.buffer).toMatchSnapshot();
254253

255-
// Reset to default
256-
prompts.updateSettings({
257-
messages: {
258-
cancel: 'Canceled',
259-
},
260-
});
254+
// Reset to original
255+
prompts.settings.messages.cancel = originalCancelMessage;
261256
});
262257

263258
test('uses global custom error message from settings', () => {
264-
prompts.updateSettings({
265-
messages: {
266-
error: 'Global error message',
267-
},
268-
});
259+
// Store original message
260+
const originalErrorMessage = prompts.settings.messages.error;
261+
// Set custom message
262+
prompts.settings.messages.error = 'Global error message';
269263

270264
const result = prompts.spinner({ output });
271265
result.start('Test operation');
@@ -274,21 +268,18 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
274268

275269
expect(output.buffer).toMatchSnapshot();
276270

277-
// Reset to default
278-
prompts.updateSettings({
279-
messages: {
280-
error: 'Something went wrong',
281-
},
282-
});
271+
// Reset to original
272+
prompts.settings.messages.error = originalErrorMessage;
283273
});
284274

285275
test('prioritizes direct options over global settings', () => {
286-
prompts.updateSettings({
287-
messages: {
288-
cancel: 'Global cancel message',
289-
error: 'Global error message',
290-
},
291-
});
276+
// Store original messages
277+
const originalCancelMessage = prompts.settings.messages.cancel;
278+
const originalErrorMessage = prompts.settings.messages.error;
279+
280+
// Set custom global messages
281+
prompts.settings.messages.cancel = 'Global cancel message';
282+
prompts.settings.messages.error = 'Global error message';
292283

293284
const result = prompts.spinner({
294285
output,
@@ -313,13 +304,9 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
313304
processEmitter.emit('exit', 2);
314305
expect(output.buffer).toMatchSnapshot();
315306

316-
// Reset to defaults
317-
prompts.updateSettings({
318-
messages: {
319-
cancel: 'Canceled',
320-
error: 'Something went wrong',
321-
},
322-
});
307+
// Reset to original values
308+
prompts.settings.messages.cancel = originalCancelMessage;
309+
prompts.settings.messages.error = originalErrorMessage;
323310
});
324311
});
325312
});

packages/prompts/src/index.ts

Lines changed: 6 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { WriteStream } from 'node:tty';
33
import { stripVTControlCharacters as strip } from 'node:util';
44
import {
55
ConfirmPrompt,
6-
type ClackSettings as CoreClackSettings,
76
GroupMultiSelectPrompt,
87
MultiSelectPrompt,
98
PasswordPrompt,
@@ -12,14 +11,15 @@ import {
1211
type State,
1312
TextPrompt,
1413
block,
15-
updateSettings as coreUpdateSettings,
1614
isCancel,
15+
updateSettings,
16+
settings
1717
} from '@clack/core';
1818
import isUnicodeSupported from 'is-unicode-supported';
1919
import color from 'picocolors';
2020
import { cursor, erase } from 'sisteransi';
2121

22-
export { isCancel } from '@clack/core';
22+
export { isCancel, updateSettings, settings } from '@clack/core';
2323

2424
const unicode = isUnicodeSupported();
2525
const s = (c: string, fallback: string) => (unicode ? c : fallback);
@@ -779,55 +779,6 @@ export interface SpinnerResult {
779779
readonly isCancelled: boolean;
780780
}
781781

782-
// Extend the core settings interface
783-
export interface ClackSettings extends CoreClackSettings {
784-
/**
785-
* Custom messages for prompts
786-
*/
787-
messages?: {
788-
/**
789-
* Custom message to display when a spinner is cancelled
790-
* @default "Canceled"
791-
*/
792-
cancel?: string;
793-
/**
794-
* Custom message to display when a spinner encounters an error
795-
* @default "Something went wrong"
796-
*/
797-
error?: string;
798-
};
799-
}
800-
801-
/**
802-
* Global settings for default messages
803-
*/
804-
const promptsSettings = {
805-
messages: {
806-
cancel: 'Canceled',
807-
error: 'Something went wrong',
808-
},
809-
};
810-
811-
/**
812-
* Update global settings for prompts including core settings
813-
*/
814-
export function updateSettings(settings: Partial<ClackSettings>) {
815-
// Handle aliases property for core if provided
816-
if (settings.aliases) {
817-
// Only pass the aliases property to core
818-
coreUpdateSettings({ aliases: settings.aliases });
819-
}
820-
// Update prompt-specific settings
821-
if (settings.messages) {
822-
if (settings.messages.cancel) {
823-
promptsSettings.messages.cancel = settings.messages.cancel;
824-
}
825-
if (settings.messages.error) {
826-
promptsSettings.messages.error = settings.messages.error;
827-
}
828-
}
829-
}
830-
831782
export const spinner = ({
832783
indicator = 'dots',
833784
onCancel,
@@ -848,10 +799,9 @@ export const spinner = ({
848799
let _origin: number = performance.now();
849800

850801
const handleExit = (code: number) => {
851-
const msg =
852-
code > 1
853-
? (errorMessage ?? promptsSettings.messages.error)
854-
: (cancelMessage ?? promptsSettings.messages.cancel);
802+
const msg = code > 1
803+
? (errorMessage ?? settings.messages.error)
804+
: (cancelMessage ?? settings.messages.cancel);
855805
isCancelled = code === 1;
856806
if (isSpinnerActive) {
857807
stop(msg, code);

0 commit comments

Comments
 (0)