From 13784cfc9f9ad8d9f6a28c2b1071f5d8a11c0afc Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Tue, 14 Apr 2026 20:47:13 -0700 Subject: [PATCH] support NO_COLOR environment variable in dev format The dev format now checks for the NO_COLOR environment variable (https://no-color.org/) and omits ANSI escape codes when it is set. This follows the community convention adopted by many CLI tools. Fixes #302 --- index.js | 27 ++++++++++++++++--------- test/morgan.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index bb97912..16a8e87 100644 --- a/index.js +++ b/index.js @@ -186,20 +186,27 @@ morgan.format('dev', function developmentFormatLine (tokens, req, res) { ? res.statusCode : undefined - // get status color - var color = status >= 500 ? 31 // red - : status >= 400 ? 33 // yellow - : status >= 300 ? 36 // cyan - : status >= 200 ? 32 // green - : 0 // no color + // get status color (respect NO_COLOR env var) + var noColor = 'NO_COLOR' in process.env + var color = noColor ? 0 + : status >= 500 ? 31 // red + : status >= 400 ? 33 // yellow + : status >= 300 ? 36 // cyan + : status >= 200 ? 32 // green + : 0 // no color // get colored function - var fn = developmentFormatLine[color] + var fn = developmentFormatLine[noColor ? 'nocolor' : color] if (!fn) { - // compile - fn = developmentFormatLine[color] = compile('\x1b[0m:method :url \x1b[' + - color + 'm:status\x1b[0m :response-time ms - :res[content-length]\x1b[0m') + if (noColor) { + // compile without ANSI escape codes + fn = developmentFormatLine['nocolor'] = compile(':method :url :status :response-time ms - :res[content-length]') + } else { + // compile with color + fn = developmentFormatLine[color] = compile('\x1b[0m:method :url \x1b[' + + color + 'm:status\x1b[0m :response-time ms - :res[content-length]\x1b[0m') + } } return fn(tokens, req, res) diff --git a/test/morgan.js b/test/morgan.js index 6557a2b..4c247d1 100644 --- a/test/morgan.js +++ b/test/morgan.js @@ -1352,6 +1352,61 @@ describe('morgan()', function () { .expect(200, cb) }) }) + + describe('with NO_COLOR env var', function () { + before(function () { + process.env.NO_COLOR = '' + }) + + after(function () { + delete process.env.NO_COLOR + // clear cached format functions + delete morgan.dev.nocolor + }) + + it('should not include ANSI escape codes', function (done) { + var cb = after(2, function (err, res, line) { + if (err) return done(err) + assert.ok(line.indexOf('\x1b[') === -1, 'should not contain ANSI escape codes') + done() + }) + + var stream = createLineStream(function onLine (line) { + cb(null, null, line) + }) + + var server = createServer('dev', { stream: stream }, function (req, res, next) { + res.statusCode = 200 + next() + }) + + request(server) + .get('/') + .expect(200, cb) + }) + + it('should still include status code and method', function (done) { + var cb = after(2, function (err, res, line) { + if (err) return done(err) + assert.ok(line.indexOf('GET') !== -1, 'should contain method') + assert.ok(line.indexOf('200') !== -1, 'should contain status') + done() + }) + + var stream = createLineStream(function onLine (line) { + cb(null, null, line) + }) + + var server = createServer('dev', { stream: stream }, function (req, res, next) { + res.statusCode = 200 + next() + }) + + request(server) + .get('/') + .expect(200, cb) + }) + }) }) describe('short', function () {