Lessons from open-source: Have you ever used console object like this `console[consoleMethod](‘’)`?
Tags: javascript, react, opensource, nextjs
Published: 2024-03-22 21:05:17

This lesson is picked from [Next.js source code](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/output/log.ts#L19). In this article, you will learn how to create generic, resuable [console](https://developer.mozilla.org/en-US/docs/Web/API/console) methods, how Next.js leverages this way of using console object.

Console
-------

In javascript, [console](https://developer.mozilla.org/en-US/docs/Web/API/console) is object that provides access to the debugging console. The following image shows instance methods of console object.

![](https://media.licdn.com/dms/image/D4E12AQHCn3ffbhu7mQ/article-inline_image-shrink_1500_2232/0/1711140802611?e=1716422400&v=beta&t=ZWSG4zV5ovEh1EVTYscZWV6lreHpJZzAwO4hi4rG6a4)

You can find out more about these [instance methods in the MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/console#instance_methods).

Generic, reusable log.ts file in Next.js source code
----------------------------------------------------

[build/output/log.ts](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/output/log.ts#L1) has the following code:

> [_Practice the exercises based on documentation_](https://tthroo.com/) _to become an expert in Next.js_

```
import { bold, green, magenta, red, yellow, white } from '../../lib/picocolors'

export const prefixes = {
  wait: white(bold('○')),
  error: red(bold('⨯')),
  warn: yellow(bold('⚠')),
  ready: '▲', // no color
  info: white(bold(' ')),
  event: green(bold('✓')),
  trace: magenta(bold('»')),
} as const

const LOGGING\_METHOD = {
  log: 'log',
  warn: 'warn',
  error: 'error',
} as const

function prefixedLog(prefixType: keyof typeof prefixes, ...message: any\[\]) {
  if ((message\[0\] === '' || message\[0\] === undefined) && message.length === 1) {
    message.shift()
  }

  const consoleMethod: keyof typeof LOGGING\_METHOD =
    prefixType in LOGGING\_METHOD
      ? LOGGING\_METHOD\[prefixType as keyof typeof LOGGING\_METHOD\]
      : 'log'

  const prefix = prefixes\[prefixType\]
  // If there's no message, don't print the prefix but a new line
  if (message.length === 0) {
    console\[consoleMethod\]('')
  } else {
    console\[consoleMethod\](' ' + prefix, ...message)
  }
}

export function bootstrap(...message: any\[\]) {
  console.log(' ', ...message)
}

export function wait(...message: any\[\]) {
  prefixedLog('wait', ...message)
}

export function error(...message: any\[\]) {
  prefixedLog('error', ...message)
}

export function warn(...message: any\[\]) {
  prefixedLog('warn', ...message)
}

export function ready(...message: any\[\]) {
  prefixedLog('ready', ...message)
}

export function info(...message: any\[\]) {
  prefixedLog('info', ...message)
}

export function event(...message: any\[\]) {
  prefixedLog('event', ...message)
}

export function trace(...message: any\[\]) {
  prefixedLog('trace', ...message)
}

const warnOnceMessages = new Set()
export function warnOnce(...message: any\[\]) {
  if (!warnOnceMessages.has(message\[0\])) {
    warnOnceMessages.add(message.join(' '))

    warn(...message)
  }
}
```

The important question here is why bother to create such a file when you could just do console.log or console.warn, it is only few letters difference , right?

Look closer. wait, error, warn, ready, info, event, trace call a function named prefixedLog.

### prefixedLog

```
function prefixedLog(prefixType: keyof typeof prefixes, ...message: any\[\]) {
  if ((message\[0\] === '' || message\[0\] === undefined) && message.length === 1) {
    message.shift()
  }

  const consoleMethod: keyof typeof LOGGING\_METHOD =
    prefixType in LOGGING\_METHOD
      ? LOGGING\_METHOD\[prefixType as keyof typeof LOGGING\_METHOD\]
      : 'log'

  const prefix = prefixes\[prefixType\]
  // If there's no message, don't print the prefix but a new line
  if (message.length === 0) {
    console\[consoleMethod\]('')
  } else {
    console\[consoleMethod\](' ' + prefix, ...message)
  }
}
```

This function has console object instance method name as the first parameter but the catch here is the “prefix”.

```
export const prefixes = {
  wait: white(bold('○')),
  error: red(bold('⨯')),
  warn: yellow(bold('⚠')),
  ready: '▲', // no color
  info: white(bold(' ')),
  event: green(bold('✓')),
  trace: magenta(bold('»')),
} as const

const prefix = prefixes\[prefixType\]
```

prefixes object has some picocolors applied symbol that show up as part of the logs in your terminal.

Because Next.js codebase is large, this is a standard set to follow to stay consistent with the symbols shown when console instance methods such as warn,, error, wait etc., are called.

This file is imported in 62 other files across the codebase.

![used across files](https://media.licdn.com/dms/image/D4E12AQFfAAcJXyk_Ow/article-inline_image-shrink_1500_2232/0/1711140802393?e=1716422400&v=beta&t=TYLju-d8rhkHqxTLOoxCLyXgCRE69stL_wtYFbHowP0)

Conclusion:
-----------

Have you ever used console object like this console\[consoleMethod\](‘ + prefix, …message’)? I haven’t yet. When I first looked at this log.ts file in next.js source code, my thoughts were “I wouldn’t mind writing console.log or console.warn”. The important question here is why bother to create such a file when you could just do console.log or console.warn, it is only few letters difference, right? the catch is symbols that are prefixed with the console instance methods in this resuable, generic log.ts file.

I found that these methods are reused in 62 files, just to stay consistent with the symbols shown when you call certain methods. These tiny details show the importance of staying consistent across your codebase especially since this is logged to the user terminal, I would be careful too.