Skip to content

Commit afe49b4

Browse files
committed
refactor(logger): Use util.styleText when possible
In the next major when we drop Node 20 support, we can remove the ansi dependency and refactor CordovaLogger to clean up the exposed API a bit.
1 parent ec5de30 commit afe49b4

2 files changed

Lines changed: 119 additions & 49 deletions

File tree

spec/CordovaLogger.spec.js

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919

2020
const CordovaError = require('../src/CordovaError');
2121
const CordovaLogger = require('../src/CordovaLogger');
22-
const EventEmitter = require('events').EventEmitter;
22+
const { EventEmitter } = require('node:events');
2323

24+
const hasStyleText = CordovaLogger.__hasStyleText;
2425
const DEFAULT_LEVELS = ['verbose', 'normal', 'warn', 'info', 'error', 'results'];
2526

2627
describe('CordovaLogger class', function () {
@@ -119,56 +120,96 @@ describe('CordovaLogger class', function () {
119120
});
120121
});
121122

122-
describe('log method', function () {
123-
function CursorSpy (name) {
124-
const cursorMethods = ['reset', 'write', 'bold'];
125-
const spy = jasmine.createSpyObj(name, cursorMethods);
123+
if (hasStyleText) {
124+
describe('log method', function () {
125+
beforeEach(function () {
126+
logger.stdoutCursor = jasmine.createSpyObj('stdout', ['write']);
127+
logger.stderrCursor = jasmine.createSpyObj('stderr', ['write']);
128+
});
126129

127-
// Make spy methods chainable, as original Cursor acts
128-
cursorMethods.forEach(function (method) { spy[method].and.returnValue(spy); });
130+
it('Test 010 : should ignore message if severity is less than logger\'s level', function () {
131+
logger.setLevel('error').log('verbose', 'some_messgge');
132+
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
133+
expect(logger.stderrCursor.write).not.toHaveBeenCalled();
134+
});
129135

130-
spy.fg = jasmine.createSpyObj('fg', ['grey', 'yellow', 'blue', 'red']);
136+
it('Test 011 : should log everything except error messages to stdout', function () {
137+
logger.setLevel('verbose');
138+
DEFAULT_LEVELS.forEach(function (level) {
139+
logger.log(level, 'message');
140+
});
131141

132-
return spy;
133-
}
142+
expect(logger.stdoutCursor.write.calls.count()).toBe((DEFAULT_LEVELS.length - 1));
143+
expect(logger.stderrCursor.write.calls.count()).toBe(1);
144+
});
134145

135-
beforeEach(function () {
136-
logger.stdoutCursor = new CursorSpy('stdoutCursor');
137-
logger.stderrCursor = new CursorSpy('stderrCursor');
138-
});
146+
it('Test 012 : should log Error objects to stderr despite of loglevel', function () {
147+
logger.setLevel('verbose').log('verbose', new Error());
148+
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
149+
expect(logger.stderrCursor.write).toHaveBeenCalled();
150+
});
139151

140-
it('Test 010 : should ignore message if severity is less than logger\'s level', function () {
141-
logger.setLevel('error').log('verbose', 'some_messgge');
142-
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
143-
expect(logger.stderrCursor.write).not.toHaveBeenCalled();
152+
it('Test 013 : should handle CordovaError instances separately from Error ones', function () {
153+
const errorMock = new CordovaError();
154+
spyOn(errorMock, 'toString').and.returnValue('error_message');
155+
156+
logger.setLevel('verbose').log('verbose', errorMock);
157+
expect(errorMock.toString).toHaveBeenCalled();
158+
expect(logger.stderrCursor.write.calls.argsFor(0)).toMatch('Error: error_message');
159+
});
144160
});
161+
} else {
162+
describe('log method (ansi)', function () {
163+
function CursorSpy (name) {
164+
const cursorMethods = ['reset', 'write', 'bold'];
165+
const spy = jasmine.createSpyObj(name, cursorMethods);
166+
167+
// Make spy methods chainable, as original Cursor acts
168+
cursorMethods.forEach(function (method) { spy[method].and.returnValue(spy); });
169+
170+
spy.fg = jasmine.createSpyObj('fg', ['grey', 'yellow', 'blue', 'red']);
171+
172+
return spy;
173+
}
145174

146-
it('Test 011 : should log everything except error messages to stdout', function () {
147-
logger.setLevel('verbose');
148-
DEFAULT_LEVELS.forEach(function (level) {
149-
logger.log(level, 'message');
175+
beforeEach(function () {
176+
logger.stdoutCursor = new CursorSpy('stdoutCursor');
177+
logger.stderrCursor = new CursorSpy('stderrCursor');
150178
});
151179

152-
// Multiply calls number to 2 because 'write' method is get called twice (with message and EOL)
153-
expect(logger.stdoutCursor.write.calls.count()).toBe((DEFAULT_LEVELS.length - 1) * 2);
154-
expect(logger.stderrCursor.write.calls.count()).toBe(1 * 2);
155-
});
180+
it('Test 010 : should ignore message if severity is less than logger\'s level', function () {
181+
logger.setLevel('error').log('verbose', 'some_messgge');
182+
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
183+
expect(logger.stderrCursor.write).not.toHaveBeenCalled();
184+
});
156185

157-
it('Test 012 : should log Error objects to stderr despite of loglevel', function () {
158-
logger.setLevel('verbose').log('verbose', new Error());
159-
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
160-
expect(logger.stderrCursor.write).toHaveBeenCalled();
161-
});
186+
it('Test 011 : should log everything except error messages to stdout', function () {
187+
logger.setLevel('verbose');
188+
DEFAULT_LEVELS.forEach(function (level) {
189+
logger.log(level, 'message');
190+
});
191+
192+
// Multiply calls number to 2 because 'write' method is get called twice (with message and EOL)
193+
expect(logger.stdoutCursor.write.calls.count()).toBe((DEFAULT_LEVELS.length - 1) * 2);
194+
expect(logger.stderrCursor.write.calls.count()).toBe(1 * 2);
195+
});
196+
197+
it('Test 012 : should log Error objects to stderr despite of loglevel', function () {
198+
logger.setLevel('verbose').log('verbose', new Error());
199+
expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
200+
expect(logger.stderrCursor.write).toHaveBeenCalled();
201+
});
162202

163-
it('Test 013 : should handle CordovaError instances separately from Error ones', function () {
164-
const errorMock = new CordovaError();
165-
spyOn(errorMock, 'toString').and.returnValue('error_message');
203+
it('Test 013 : should handle CordovaError instances separately from Error ones', function () {
204+
const errorMock = new CordovaError();
205+
spyOn(errorMock, 'toString').and.returnValue('error_message');
166206

167-
logger.setLevel('verbose').log('verbose', errorMock);
168-
expect(errorMock.toString).toHaveBeenCalled();
169-
expect(logger.stderrCursor.write.calls.argsFor(0)).toMatch('Error: error_message');
207+
logger.setLevel('verbose').log('verbose', errorMock);
208+
expect(errorMock.toString).toHaveBeenCalled();
209+
expect(logger.stderrCursor.write.calls.argsFor(0)).toMatch('Error: error_message');
210+
});
170211
});
171-
});
212+
}
172213

173214
describe('adjustLevel method', function () {
174215
it('Test 014 : should properly adjust log level', function () {

src/CordovaLogger.js

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,22 @@
1717
under the License.
1818
*/
1919

20-
const ansi = require('ansi');
21-
const EventEmitter = require('events').EventEmitter;
22-
const EOL = require('os').EOL;
20+
const { EventEmitter } = require('node:events');
21+
const { EOL } = require('node:os');
22+
const util = require('node:util');
2323
const formatError = require('./util/formatError');
2424

25+
const hasStyleText = (function () {
26+
try {
27+
if (util.styleText(['bold', 'red'], 'test') !== '') {
28+
return true;
29+
}
30+
} catch { }
31+
return false;
32+
})();
33+
34+
const ansi = !hasStyleText ? require('ansi') : null;
35+
2536
const INSTANCE_KEY = Symbol.for('org.apache.cordova.common.CordovaLogger');
2637

2738
/**
@@ -48,6 +59,11 @@ class CordovaLogger {
4859

4960
static get RESULTS () { return 'results'; }
5061

62+
/**
63+
* @private This is just exposed temporarily for unit testing.
64+
*/
65+
static get __hasStyleText () { return hasStyleText; }
66+
5167
/**
5268
* Static method to create new or acquire existing instance.
5369
*
@@ -74,10 +90,17 @@ class CordovaLogger {
7490
/** @private */
7591
this.stderr = process.stderr;
7692

77-
/** @private */
78-
this.stdoutCursor = ansi(this.stdout);
79-
/** @private */
80-
this.stderrCursor = ansi(this.stderr);
93+
if (hasStyleText) {
94+
/** @private */
95+
this.stdoutCursor = this.stdout;
96+
/** @private */
97+
this.stderrCursor = this.stderr;
98+
} else {
99+
/** @private */
100+
this.stdoutCursor = ansi(this.stdout);
101+
/** @private */
102+
this.stderrCursor = ansi(this.stderr);
103+
}
81104

82105
this.addLevel(CordovaLogger.VERBOSE, 1000, 'grey');
83106
this.addLevel(CordovaLogger.NORMAL, 2000);
@@ -120,11 +143,17 @@ class CordovaLogger {
120143
}
121144

122145
const color = this.colors[logLevel];
123-
if (color) {
124-
cursor.bold().fg[color]();
125-
}
126146

127-
cursor.write(message).reset().write(EOL);
147+
if (!hasStyleText) {
148+
if (color) {
149+
cursor.bold().fg[color]();
150+
}
151+
152+
cursor.write(message).reset().write(EOL);
153+
} else {
154+
const styles = color ? ['bold', color] : [];
155+
cursor.write(util.styleText(styles, message) + EOL);
156+
}
128157

129158
return this;
130159
}

0 commit comments

Comments
 (0)