Skip to content

Commit dcc8d05

Browse files
committed
fix: create spinner-aware logger to more easily avoid jumbled logs
spinner.interrupt functionality already existed, but it required code to manually use it. createLogger now accepts a spinner, so any calls to such a logger will automatically go through spinner.interrupt for safe logging when a spinner is present
1 parent 3eb23d9 commit dcc8d05

File tree

4 files changed

+47
-15
lines changed

4 files changed

+47
-15
lines changed

src/commands/sourcemaps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ sourcemapsCommand
171171
sourcemapsCommand.error(COMMON_ERROR_MESSAGES.REALM_NOT_SPECIFIED);
172172
}
173173

174-
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO);
175174
const spinner = createSpinner();
175+
const logger = createLogger(options.debug ? LogLevel.DEBUG : LogLevel.INFO, spinner);
176176
try {
177177
await runSourcemapUpload({ ...options, directory: options.path }, { logger, spinner });
178178
} catch (e) {

src/sourcemaps/index.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -187,24 +187,18 @@ export async function runSourcemapUpload(options: SourceMapUploadOptions, ctx: S
187187
// eslint-disable-next-line @typescript-eslint/no-unused-vars
188188
].filter(([_, value]) => typeof value !== 'undefined'));
189189

190-
spinner.interrupt(() => {
191-
logger.debug('Uploading %s', path);
192-
logger.debug('PUT', url);
193-
});
190+
logger.debug('Uploading %s', path);
191+
logger.debug('PUT', url);
194192

195193
const dryRunUploadFile: typeof uploadFile = async () => {
196-
spinner.interrupt( () => {
197-
logger.info('sourceMapId %s would be used to upload %s', sourceMapId, path);
198-
});
194+
logger.info('sourceMapId %s would be used to upload %s', sourceMapId, path);
199195
};
200196
const uploadFileFn = options.dryRun ? dryRunUploadFile : uploadFile;
201197

202198
// notify user if we cannot be certain the "sourcemaps inject" command was already run
203199
const alreadyInjected = await wasInjectAlreadyRun(path, logger);
204200
if (!alreadyInjected.result) {
205-
spinner.interrupt(() => {
206-
logger.warn(alreadyInjected.message);
207-
});
201+
logger.warn(alreadyInjected.message);
208202
}
209203

210204
// upload a single file

src/utils/logger.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import chalk from 'chalk';
18+
import { Spinner } from './spinner';
1819

1920
/** Logger methods can be called just like console.log */
2021
export interface Logger {
@@ -31,14 +32,27 @@ export const enum LogLevel {
3132
DEBUG = 1
3233
}
3334

34-
export function createLogger(logLevel: LogLevel): Logger {
35+
export function createLogger(logLevel: LogLevel, spinner?: Spinner): Logger {
3536
// Send info to stdout, and all other logs to stderr
36-
return {
37+
const basicLogger = {
3738
error: (msg, ...params) => LogLevel.ERROR >= logLevel && prefixedConsoleError(chalk.stderr.red('ERROR '), msg, ...params),
3839
warn: (msg, ...params) => LogLevel.WARN >= logLevel && prefixedConsoleError(chalk.stderr.yellow('WARN '), msg, ...params),
3940
info: (msg, ...params) => LogLevel.INFO >= logLevel && console.log(msg, ...params),
4041
debug: (msg, ...params) => LogLevel.DEBUG >= logLevel && prefixedConsoleError(chalk.stderr.gray('DEBUG '), msg, ...params),
4142
} as Logger;
43+
44+
if (spinner) {
45+
// wrap logging functions with spinner.interrupt() to avoid jumbled logs when the spinner is active
46+
const spinnerAwareLogger = {
47+
error: (...args) => { spinner.interrupt(() => basicLogger.error(...args)); },
48+
warn: (...args) => { spinner.interrupt(() => basicLogger.warn(...args)); },
49+
info: (...args) => { spinner.interrupt(() => basicLogger.info(...args)); },
50+
debug: (...args) => { spinner.interrupt(() => basicLogger.debug(...args)); },
51+
} as Logger;
52+
return spinnerAwareLogger;
53+
} else {
54+
return basicLogger;
55+
}
4256
}
4357

4458
/** Carefully wrap console.error so the logger can properly support format strings */

test/utils/logger.test.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
*/
1616

1717
import { createLogger, LogLevel } from '../../src/utils/logger';
18+
import { createSpinner, Spinner } from '../../src/utils/spinner';
1819

1920
describe('createLogger', () => {
2021

21-
test('should respect log level', () => {
22+
test.each([
23+
[undefined], /* test case: normal logger (without a spinner) */
24+
[createSpinner()] /* test case: spinner-aware logger */
25+
])('should respect log level', (spinner: Spinner | undefined) => {
2226
const output: unknown[] = [];
2327
const logMock = jest.spyOn(console, 'log').mockImplementation((arg: unknown) => output.push(arg));
2428
const errorMock = jest.spyOn(console, 'error').mockImplementation((arg: unknown) => output.push(arg));
@@ -31,7 +35,7 @@ describe('createLogger', () => {
3135
]);
3236

3337
for (const [ level, label ] of levels.entries()) {
34-
const logger = createLogger(level);
38+
const logger = createLogger(level, spinner);
3539
logger.error(`${label}.error`);
3640
logger.warn(`${label}.warn`);
3741
logger.info(`${label}.info`);
@@ -86,4 +90,24 @@ describe('createLogger', () => {
8690
errorMock.mockRestore();
8791
});
8892

93+
test('should invoke spinner.interrupt() before logging, if a spinner is provided', () => {
94+
const output: unknown[] = [];
95+
const logMock = jest.spyOn(console, 'log').mockImplementation((arg: unknown) => output.push(arg));
96+
const errorMock = jest.spyOn(console, 'error').mockImplementation((arg: unknown) => output.push(arg));
97+
const spinner = createSpinner();
98+
jest.spyOn(spinner, 'interrupt');
99+
100+
const logger = createLogger(LogLevel.DEBUG, spinner);
101+
102+
logger.error('error with spinner');
103+
logger.warn('warn with spinner');
104+
logger.info('info with spinner');
105+
logger.debug('debug with spinner');
106+
107+
expect(spinner.interrupt).toHaveBeenCalledTimes(4);
108+
109+
logMock.mockRestore();
110+
errorMock.mockRestore();
111+
});
112+
89113
});

0 commit comments

Comments
 (0)