diff --git a/deno_dist/helper/dev/index.ts b/deno_dist/helper/dev/index.ts index dba838388..1898c16b8 100644 --- a/deno_dist/helper/dev/index.ts +++ b/deno_dist/helper/dev/index.ts @@ -1,5 +1,6 @@ import type { Hono } from '../../hono.ts' import type { Env, RouterRoute } from '../../types.ts' +import { getColorEnabled } from '../../utils/color.ts' import { findTargetHandler, isMiddleware } from '../../utils/handler.ts' interface ShowRoutesOptions { @@ -31,16 +32,7 @@ export const inspectRoutes = (hono: Hono): RouteData[] => { } export const showRoutes = (hono: Hono, opts?: ShowRoutesOptions) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { process, Deno } = globalThis as any - const isNoColor = - typeof process !== 'undefined' - ? // eslint-disable-next-line no-unsafe-optional-chaining - 'NO_COLOR' in process?.env - : typeof Deno?.noColor === 'boolean' - ? (Deno.noColor as boolean) - : false - const colorEnabled = opts?.colorize ?? !isNoColor + const colorEnabled = opts?.colorize ?? getColorEnabled() const routeData: Record = {} let maxMethodLength = 0 let maxPathLength = 0 diff --git a/deno_dist/middleware/logger/index.ts b/deno_dist/middleware/logger/index.ts index 16375f1fa..2d10050f8 100644 --- a/deno_dist/middleware/logger/index.ts +++ b/deno_dist/middleware/logger/index.ts @@ -1,4 +1,5 @@ import type { MiddlewareHandler } from '../../types.ts' +import { getColorEnabled } from '../../utils/color.ts' import { getPath } from '../../utils/url.ts' enum LogPrefix { @@ -21,14 +22,15 @@ const time = (start: number) => { } const colorStatus = (status: number) => { + const colorEnabled = getColorEnabled() const out: { [key: string]: string } = { - 7: `\x1b[35m${status}\x1b[0m`, - 5: `\x1b[31m${status}\x1b[0m`, - 4: `\x1b[33m${status}\x1b[0m`, - 3: `\x1b[36m${status}\x1b[0m`, - 2: `\x1b[32m${status}\x1b[0m`, - 1: `\x1b[32m${status}\x1b[0m`, - 0: `\x1b[33m${status}\x1b[0m`, + 7: colorEnabled ? `\x1b[35m${status}\x1b[0m` : `${status}`, + 5: colorEnabled ? `\x1b[31m${status}\x1b[0m` : `${status}`, + 4: colorEnabled ? `\x1b[33m${status}\x1b[0m` : `${status}`, + 3: colorEnabled ? `\x1b[36m${status}\x1b[0m` : `${status}`, + 2: colorEnabled ? `\x1b[32m${status}\x1b[0m` : `${status}`, + 1: colorEnabled ? `\x1b[32m${status}\x1b[0m` : `${status}`, + 0: colorEnabled ? `\x1b[33m${status}\x1b[0m` : `${status}`, } const calculateStatus = (status / 100) | 0 diff --git a/deno_dist/utils/color.ts b/deno_dist/utils/color.ts new file mode 100644 index 000000000..a51909eb5 --- /dev/null +++ b/deno_dist/utils/color.ts @@ -0,0 +1,12 @@ +export function getColorEnabled() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { process, Deno } = globalThis as any + const isNoColor = + typeof process !== 'undefined' + ? // eslint-disable-next-line no-unsafe-optional-chaining + 'NO_COLOR' in process?.env + : typeof Deno?.noColor === 'boolean' + ? (Deno.noColor as boolean) + : false + return !isNoColor +} diff --git a/src/helper/dev/index.ts b/src/helper/dev/index.ts index 70e29a7be..3393aeacd 100644 --- a/src/helper/dev/index.ts +++ b/src/helper/dev/index.ts @@ -1,5 +1,6 @@ import type { Hono } from '../../hono' import type { Env, RouterRoute } from '../../types' +import { getColorEnabled } from '../../utils/color' import { findTargetHandler, isMiddleware } from '../../utils/handler' interface ShowRoutesOptions { @@ -31,16 +32,7 @@ export const inspectRoutes = (hono: Hono): RouteData[] => { } export const showRoutes = (hono: Hono, opts?: ShowRoutesOptions) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { process, Deno } = globalThis as any - const isNoColor = - typeof process !== 'undefined' - ? // eslint-disable-next-line no-unsafe-optional-chaining - 'NO_COLOR' in process?.env - : typeof Deno?.noColor === 'boolean' - ? (Deno.noColor as boolean) - : false - const colorEnabled = opts?.colorize ?? !isNoColor + const colorEnabled = opts?.colorize ?? getColorEnabled() const routeData: Record = {} let maxMethodLength = 0 let maxPathLength = 0 diff --git a/src/middleware/logger/index.test.ts b/src/middleware/logger/index.test.ts index b1b1d09b1..fbb37973f 100644 --- a/src/middleware/logger/index.test.ts +++ b/src/middleware/logger/index.test.ts @@ -74,3 +74,80 @@ describe('Logger by Middleware', () => { expect(log).toMatch(/m?s$/) }) }) + +describe('Logger by Middleware in NO_COLOR', () => { + let app: Hono + let log: string + + beforeEach(() => { + vi.stubEnv('NO_COLOR', '1') + function sleep(time: number) { + return new Promise((resolve) => setTimeout(resolve, time)) + } + + app = new Hono() + + const logFn = (str: string) => { + log = str + } + + const shortRandomString = 'hono' + const longRandomString = 'hono'.repeat(1000) + + app.use('*', logger(logFn)) + app.get('/short', (c) => c.text(shortRandomString)) + app.get('/long', (c) => c.text(longRandomString)) + app.get('/seconds', async (c) => { + await sleep(1000) + + return c.text(longRandomString) + }) + app.get('/empty', (c) => c.text('')) + }) + afterAll(() => { + vi.unstubAllEnvs() + }) + it('Log status 200 with empty body', async () => { + const res = await app.request('http://localhost/empty') + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(log.startsWith(' --> GET /empty 200')).toBe(true) + expect(log).toMatch(/m?s$/) + }) + + it('Log status 200 with small body', async () => { + const res = await app.request('http://localhost/short') + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(log.startsWith(' --> GET /short 200')).toBe(true) + expect(log).toMatch(/m?s$/) + }) + + it('Log status 200 with big body', async () => { + const res = await app.request('http://localhost/long') + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(log.startsWith(' --> GET /long 200')).toBe(true) + expect(log).toMatch(/m?s$/) + }) + + it('Time in seconds', async () => { + const res = await app.request('http://localhost/seconds') + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(log.startsWith(' --> GET /seconds 200')).toBe(true) + expect(log).toMatch(/1s/) + }) + + it('Log status 404', async () => { + const msg = 'Default 404 Not Found' + app.all('*', (c) => { + return c.text(msg, 404) + }) + const res = await app.request('http://localhost/notfound') + expect(res).not.toBeNull() + expect(res.status).toBe(404) + expect(log.startsWith(' --> GET /notfound 404')).toBe(true) + expect(log).toMatch(/m?s$/) + }) +}) diff --git a/src/middleware/logger/index.ts b/src/middleware/logger/index.ts index 18fb8f2e9..28e0c9b19 100644 --- a/src/middleware/logger/index.ts +++ b/src/middleware/logger/index.ts @@ -1,4 +1,5 @@ import type { MiddlewareHandler } from '../../types' +import { getColorEnabled } from '../../utils/color' import { getPath } from '../../utils/url' enum LogPrefix { @@ -21,14 +22,15 @@ const time = (start: number) => { } const colorStatus = (status: number) => { + const colorEnabled = getColorEnabled() const out: { [key: string]: string } = { - 7: `\x1b[35m${status}\x1b[0m`, - 5: `\x1b[31m${status}\x1b[0m`, - 4: `\x1b[33m${status}\x1b[0m`, - 3: `\x1b[36m${status}\x1b[0m`, - 2: `\x1b[32m${status}\x1b[0m`, - 1: `\x1b[32m${status}\x1b[0m`, - 0: `\x1b[33m${status}\x1b[0m`, + 7: colorEnabled ? `\x1b[35m${status}\x1b[0m` : `${status}`, + 5: colorEnabled ? `\x1b[31m${status}\x1b[0m` : `${status}`, + 4: colorEnabled ? `\x1b[33m${status}\x1b[0m` : `${status}`, + 3: colorEnabled ? `\x1b[36m${status}\x1b[0m` : `${status}`, + 2: colorEnabled ? `\x1b[32m${status}\x1b[0m` : `${status}`, + 1: colorEnabled ? `\x1b[32m${status}\x1b[0m` : `${status}`, + 0: colorEnabled ? `\x1b[33m${status}\x1b[0m` : `${status}`, } const calculateStatus = (status / 100) | 0 diff --git a/src/utils/color.test.ts b/src/utils/color.test.ts new file mode 100644 index 000000000..d0d2aac44 --- /dev/null +++ b/src/utils/color.test.ts @@ -0,0 +1,18 @@ +import { getColorEnabled } from './color' + +describe('getColorEnabled()', () => { + it('getColorEnabled() is true', async () => { + expect(getColorEnabled()).toBe(true) + }) +}) +describe('getColorEnabled() in NO_COLOR', () => { + beforeAll(() => { + vi.stubEnv('NO_COLOR', '1') + }) + afterAll(() => { + vi.unstubAllEnvs() + }) + it('getColorEnabled() is false', async () => { + expect(getColorEnabled()).toBe(false) + }) +}) diff --git a/src/utils/color.ts b/src/utils/color.ts new file mode 100644 index 000000000..a51909eb5 --- /dev/null +++ b/src/utils/color.ts @@ -0,0 +1,12 @@ +export function getColorEnabled() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { process, Deno } = globalThis as any + const isNoColor = + typeof process !== 'undefined' + ? // eslint-disable-next-line no-unsafe-optional-chaining + 'NO_COLOR' in process?.env + : typeof Deno?.noColor === 'boolean' + ? (Deno.noColor as boolean) + : false + return !isNoColor +}