Check out "Do you speak JavaScript?" - my latest video course on advanced JavaScript.
Language APIs, Popular Concepts, Design Patterns, Advanced Techniques In the Browser

I've made an Express.js based source viewer

I've made an Express.js based source viewer Photo by Robert Linder

It's unsurprising that we all use Express.js when we need a web server. In this article, I'll show you how I built a source viewer. It's distributed as a npm package view-source and it could render the content of a directory in a nice tree-view style.

The inspiration behind this small library was the need to show one of my private projects through the web. I didn't want to give access to my GitHub repository, so I figured that building a tool for that would be nice.

Here's the end result:

view-source

First I started with the idea of writing an Express.js handler. Something like this:

const { viewSource } = require('view-source');

app.get('/project', viewSource(__dirname + '/path/to/code'));

However, I quickly realized that I needed to do a couple of other things:

  • I'll need some basic HTML
  • There will be some assets that need to be served statically (icons + JavaScript and styles for the syntax highlighter)
  • There will be some client-side JavaScript for the tree view on the left.

So, I can't use a handler. I need access to the Express.js application instance. That's why I ended up with the following API:

const express = require('express');
const { viewSource } = require('view-source');

const app = express();

viewSource({
  appTitle: 'My App Name',
  app,
  route: '/code',
  source: __dirname + '/../'
});

Where the route is the path at which my little app will be served, and the source is the actual physical path to the files.

Internally, the viewSource function registers two paths for static resources using express.static helper. The first one is for all the assets the viewer needs, and the second one is for the actual files we want to show.

After that, the function recursively goes over the provided source path. It builds a JSON structure with all the files and folders. That structure is later sent to the client directly with the HTML code of the viewer. It's important to note here that the traversing of the source happens only once in the beginning when the server starts. Here's the code that does that:

function buildTree() {
  const root = {
    name: appTitle || defaultAppTitle,
    path: path.normalize(route),
    type: 'directory',
    children: []
  };
  function traverseDirectory(dirPath, parentNode) {
    const files = fs.readdirSync(dirPath);
    files.forEach(file => {
      const filePath = path.join(dirPath, file);
      const stats = fs.statSync(filePath);
      const node = {
        name: file,
        path: filePath.replace(ROOT_DIR, ''),
        type: stats.isDirectory() ? 'directory' : 'file',
        children: []
      };

      if (stats.isDirectory()) {
        traverseDirectory(filePath, node);
      }

      parentNode.children.push(node);
    });
  }
  traverseDirectory(ROOT_DIR, root);

  return root;
}

(To get more context you may need to open the whole file. It's here)

On the client, I decided to go simple and wrote ~80 lines of JavaScript controlling the screen's left side (the tree view). On the back-end side, the JSON that is getting built contains information if the entry is a file or a directory. That's so the client could expand and collapse folders. If the user clicks on a file, that file is getting loaded via fetch as a text and shown on the right side of the screen.

I was tempted to paste all the code, but maybe that could be more interesting. You can see it here. The highlighting is worth mentioning. I decided to use Prism. Usually, the library works on page load, meaning the code that needs to be colored exists on the page. However, here that's not the case. Thankfully Prism has an API for such cases:

function renderCode(code, ext = 'js') {
  document.querySelector('#code').innerHTML = `
  <pre><code class="language-${ext}">${encodeHTMLTags(code)}</code></pre>
  `;
  setTimeout(() => Prism.highlightAll(), 10);
}

(The setTimeout is to make sure that everything is properly set in the HTML)


So, if you ever need to show someone your code and you want to do it using your Express.js server consider view-source package.

If you enjoy this post, share it on Twitter, Facebook or LinkedIn.