The Markdown Parser

12 May

And he’s back!

I’m back at CDOT for the summer! A massive thanks to David Humphrey, Dawn Mercer and the Mozilla Webmaker team for giving me another opportunity to contribute in such a focused manner.

I’m going to keep this post as brief as I can. My re-introduction to web development came in the form of a challenge. I was to:

  • Build a node module that exposes a method for parsing the links in Markdown syntax
  • Make this module bower-compatible
  • Make this module cross-platform (browser & nodejs)
  • Demonstrate it’s use on the server-side by incorporating a command-line tool that reads Markdown from a file
  • Demonstrate it’s use on the client-side by building a github-pages website that uses it with requirejs and bower
  • Demonstrate some grasp of LESS by making that website all pretty ‘n stuff

Right.

The Nodejs module

This was a good exercise, because it forced me to become reacquainted with how Nodejs logically organizes code that uses it as a framework. Using the CommonJS module system meant putting my parsing logic in a file with this structure:

module.exports = {
  // Logic here
};

It also meant using the package.json syntax necessary for publishing an NPM module. This was nice review.

Cross-compatibility

Once my parsing logic was complete, I had to figure out a way to make the same file compatible with the RequireJS and CommonJS module systems simultaneously. This was accomplished by encapsulating the module.exports code from earlier into a method named define() so as to keep RequireJS happy:

if (typeof define !== 'function') { 
  var define = require('amdefine')(module);
}

define(function( require ) {
  return {
    // Logic here
  };
});

Command line

This was a fairly simple task, involving reading data from a file (specified as a command line argument) into utf8 format:

fs.readfile( path, { encoding: "utf8" }, function( err, data ) {
  // Call my parser and print output here
});

Client-side demo

This can be viewed at http://sedge.github.io/mdlinkparser/. The challenge was approaching this as if it were a completely isolated web application, that just happened to need to use a Markdown link parser. From this perspective, I would need:

  • A front end package manager (bower)
  • A front end module loading system (RequireJS), and
  • A build system to connect the two as part of my workflow (grunt)

Configuring bower

Bower is powerful. As a package manager, it shares many similarities with Nodejs’ package manager NPM. For instance, bower has a bower.json configuration file that operates in a similar way to NPM’s package.json. Mine ended up looking like this:

{
  "name": "mdlinkparser",
  "dependencies": {
    "sedge_mdlinkparser": "1.0.2",
    "jquery": "2.1.1"
  }
}

I leveraged bower further by adding an automatic build step by specifying a postinstall script that called grunt. I’ll get to this in a moment.

Configuring RequireJS

RequireJS is awesome because it ensures that the modules you need are fully loaded, with all of their dependencies included, before running your program logic. It has a simple structure for specifying which modules to load:

require( [ "dependencyName" ], function( dependencyName ) {
    // Logic using `dependencyName` goes here
  });
});

However! The javascript files that are used on a public facing website can have complex folder hierarchies, meaning that some work has to be done before a dependency can just be specified by name like in my previous example. Manually, it involves running require.config() before any RequireJS logic in order to establish symbolic links to the actual resources:

require.config({
  paths: {
    dependencyName: "path/to/src/dependencyName.js"
  }
});

Being on a grunt trip, I decided I would automate the process. I found a grunt plugin, called grunt-bower-requirejs, that just needed to be pointed to the file that would run require.config() and would automatically configure the paths for me. This meant that I now had a Nodejs-based build system using grunt for a front-end bower-based system using Requirejs.

Running was a simple as bower install, since bower would then call grunt because of the script I specified in the file called .bowerrc:

{
  "directory": "js",
  "scripts": {
    "postinstall": "grunt build"
  }
}

Conclusion

I didn’t get to dive into LESS scripting for front end styling, but I hope to soon. I also spent a lot of time making sure my fellow CDOT developers were managing to keep up, and everyone seemed to learn the essentials as a result.

Leave a comment