Use PrismJS for syntax highlighting in console.log in NodeJS
Tags: node, prismjs, tools
Published: 2020-06-16 23:51:11

---
title: Use PrismJS for syntax highlighting in console.log in NodeJS
published: true
description: This article show how PrismJS can be used to highlight code via console.log
tags: NodeJS, Node, PrismJS, Tools
//cover_image: https://i2.wp.com/n8d.at/wp-content/uploads/2020/06/Screenshot-2020-06-14-at-22.47.47.png
---

<img src="https://n8d.at/wp-content/uploads/2020/06/Screenshot-2020-06-14-at-22.47.47-1024x518.png" alt="" width="900" height="455" class="aligncenter size-large wp-image-52905" />

With [Markshell](https://www.npmjs.com/package/markshell) I created a small tool that allows you to output Markdown files directly to the console. Why that? Right now, when you write a CLI or any console application, you like to provide some help for it. On the other hand, you also want to have proper documentation set up on Github pages or only in the Github Repo.

[Markshell](https://www.npmjs.com/package/markshell) is precisely for that and helps and provides this opportunity and helps you to avoid writing multiple documentations.

The first version released focused more on the overall output of a Markdown file written to the console. The new version 0.0.5 now also supports source code highlighting inside the documentation. What could be a better match than to use something lightweight like the amazing PrismJS?

## PrimsJS for web sites now for NodeJS

[Prismjs](https://prismjs.com) is an excellent syntax highlighter for the web but is not explicitly made to output highlighted source code on the console. Nevertheless, the algorithm of how this tool highlights source code on a website can get used for console.log too.
To perform this transformation, the first thing to do is to install the PrismJS package.

```shell
npm install prismjs --save
```

To format the source code, then only needs two things.

```javascript
// require prismjs
const prismjs = require('prismjs');

const language = 'javascript';

// highlight source code
const prismCode = prismjs.highlight("console.log('Hello world')", Prism.languages[language], language);
```

The highlight function takes three arguments. The first is the source code to highlight, the second argument is the grammar, and finally, the language.

To highlight, for example `console.log('Hello world')`, this is the first argument as a string. The second and third are defined by the language to use for highlighting. In the previous example, it is 'javascript'.

The result returned by the PrismJS is HTML that normally would get rendered on a web site. Since we cannot output the HTML directly to the console, the HTML needs a transformation. 
The returned HTML contains only <span> -Elements and class names that describe things like keywords, strings, comments, ... and so on.

## Transform theme CSS to console color using chalks

Prismjs use for the highlighting CSS. For `console.log` we need a tool named [Chalk](https://www.npmjs.com/package/chalk) to color the output. 

A theme for PrismJS contains the following definitions.

```css
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
    color: slategray;
}

.token.punctuation {
    color: #999;
}

.token.namespace {
    opacity: .7;
}

.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
    color: #905;
}
```

So a span always contains two classes—a 'token' class followed by a descriptive more class. The second defines the output colour. This CSS can get transferred in a somewhat stylesheet for JavaScipt.

```javascript
const chalk = require("chalk");

var theme = {};
theme.token = {};

theme.background = chalk.bgHex("#f5dfd0");

theme.token["comment"] = theme.background.keyword('slategray');
theme.token["prolog"] = theme.background.keyword('slategray');
theme.token["doctype"] = theme.background.keyword('slategray');
theme.token["cdata"] = theme.background.keyword('slategray');

theme.token["punctuation"] = theme.background.hex("#999");

theme.token["namespace"];

theme.token["property"] = theme.background.hex('#905');
theme.token["tag"] = theme.background.hex('#905');
theme.token["constant"] = theme.background.hex('#905');
theme.token["symbol"] = theme.background.hex('#905');
theme.token["deleted"] = theme.background.hex('#905');
```
With this style definition gives use all needed for the console output.

## Convert HTML to console.log output

Now that we have the output styles ready and the HTML output. The only thing left is to run over the HTML. NodeJS does not contain any classes to manipulate the DOM structures because the primary use is backend code.

The solution is to add another npm package that provides the HTML Document Object Model, and it is named [JSDom](https://www.npmjs.com/package/jsdom).

```javascript
const jsdom = require("jsdom");
const {
    JSDOM
} = jsdom;
```

First, it needs to be required by the script after that it could be used.

```javascript
 // Parse source code and return HTML from PrismJS output
 const prismCode = prismjs.highlight(source, Prism.languages[language], language);

 // load HTML fragment
 const dom = JSDOM.fragment(prismCode);

var highlightedSource = parseFormatedContent(dom.childNodes, 0);
```

So the prismCode gets converted in a new [HTML document fragment](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment). This minimal document structure recursively parsed and replaced into Chalk wrap text junks does the following code.

```javascript
const parseFormatedContent = (domElement, recLevel) = > {

    let highlightedSource = ""

    domElement.forEach((element, index) => {

            if (element.hasChildNodes()) {

                let hlCode = getHighlightToken(element.classList);
                highlightedSource += hlCode(parseFormatedContent(element.childNodes, recLevel + 1));

            } else {

                highlightedSource += element.textContent;

            }

        }

    );

    return highlightedSource;

}
```
SPAN that contains no child nodes gets rendered directly out; all other gets processed again no further child elements get found. 
To replace and wrap the content with the correct styles the class list gets passed to another function named 'getHighlightToken'.

```javascript
const getHighlightToken = (tokens) => {

    let tokenFound = null;

    for (let i = 0; i < tokens.length; i++) {

        if (themeTokenKeys.indexOf(tokens[i]) !== -1) {

            tokenFound = theme.token[tokens[i]];
            break;
        }

    }

    if (tokenFound !== null) {

        return tokenFound;

    } else {

        return (content) => {
            return content
        };

    }

}
```

Once a matching style definition found, it returns the chalk function that needs to wrap the inner text and controls the output.

The last thing to do is to take the overall result of the function 'parseFormatedContent' need to be printed to the console using 'console.log'.

## The Result on the console

The following example shows the console outputs based on different themes.

[caption id="attachment_52901" align="aligncenter" width="900"]<img src="https://n8d.at/wp-content/uploads/2020/06/okaido-output-951x1024.png" alt="" width="900" height="969" class="size-large wp-image-52901" /> Console output using Okaido theme[/caption]

[caption id="attachment_52902" align="aligncenter" width="900"]<img src="https://n8d.at/wp-content/uploads/2020/06/funky-output-997x1024.png" alt="" width="900" height="924" class="size-large wp-image-52902" /> Console output using Funky theme[/caption]

[caption id="attachment_52903" align="aligncenter" width="900"]<img src="https://n8d.at/wp-content/uploads/2020/06/tomorrow-output-905x1024.png" alt="" width="900" height="1018" class="size-large wp-image-52903" /> Console output using Tomorrow theme[/caption]


## Verdict

I love PrismJS not only on the web but also in the here presented way to output source code formatted on the console. With not much effort it allows me to highlight 253 different programming languages.

Special kudos to [Lea Verou](https://twitter.com/LeaVerou) to bring this simple, lightweight tool to life.
It is more than useful on the web and in NodeJS too.

The complete code for this can be found on Github in my [Markshell project](https://github.com/stfbauer/markshell#readme)

</span>