Category: Node.js
Mar 4, 2019

I love using YUIDoc for my JavaScript projects because the setup is dead simple and the default theme has everything I need, including search and good markup for SEO. It’s no secret it’s a little outdated, though (does Yahoo! even still exist?). If you’re using more modern JavaScript practices like CommonJS modules, you’ll probably run into issues with its interpretation of a “module” versus a “class”–two concepts that have evolved wildly in their practical usage in JavaScript since YUIDoc was created.

Suffice to say, “module” in YUIDoc doesn’t mean “module” in the import/export/require sense, especially when you’re splitting your code out into smaller files or using something like Browserify to bundle your project. Classes didn’t even technically exist (at least in name) until ECMAScript 2015. So how do you document a module such that it shows up nicely in your documentation?

Static vs. Constructor Classes

What you’re probably looking for is a “static class.” YUIDoc’s syntax reference notes that whenever you define a @class you should include a @static or @constructor tag. The @constructor tag’s use is self-evident in the traditional sense of a class, whereas the @static tag “indicates that you should not instantiate the class with new. You can call all of the class’s methods statically.” That sounds kind of like what we’re doing when requiring a module!

Assuming you’re exporting an object with methods, you can document the module as a static class and then write up each of its methods with the @method tag. Place a block like so at the top of your module:

/**
 * Provides sorting tools for Posts instances
 *
 * @class sort
 * @static
 * @uses validatePostArray
 */

Then document each of the methods of the returned object like so:

/**
 * Sorts posts in a Posts instance by their
 * pubDate property, descending.
 *
 * @example
 * 	sort.pubDateNewest(Posts);
 *
 * @method pubDateNewest
 *
 * @param {Posts} posts Posts instance
 * @return {Posts} sorted Posts instance
 */
Methods indexed at same level of importance in module documentation

But what if you’re not exporting an object with methods?

Exporting a Function

If you’re exporting a single function (with or without @private methods in the module) you’ll need a workaround, though. A @class tag will not respect the usual @method tags like @param and @return.

In this case, what I recommend is placing your @class block, then immediately below it placing an @method block with the method name matching your module name. Document the function you’re returning as a normal method, then in your class block, write in the description that the class is a module that exports that method, and link to the method with an anchor tag.

/**
 * CommonJS module - exports <a href="#method_fetchXML">fetchXML</a>
 *
 * @class fetchXML
 * @static
 */

/**
 * Fetches an XMLDocument instance from a URL.
 * 
 * @example
 * 	let xmlDoc = await require("path/fetchXML.js")("https://example.com/feed");
 *
 * @method fetchXML
 * @async
 * @chainable
 * @param {String} url URL of feed to retrieve
 * @return {XMLDocument} traversable XMLDocument instance
 */

This ensures that the exported function has a clear, expected name and takes a place front-and-center in your module’s documentation. Clicking the link in the class description will immediately open the documentation for your exported function.

Exported function takes a priority spot in the “class” description

Using this formatting will allow you to document any range of CommonJS modules and point clearly to your exports while ensuring they show up in the index on the left.

The future of YUIDoc

YUIDoc has been around for a while and has a lot of competitors like JSDoc, ESDoc, Doxx, Docco, Document! X, and Natural Docs. While its functionality is probably not going anywhere any time soon, I’m interested to hear what documentation tools you use and how they address modern JavaScript. Feel free to leave a comment or send me an email on this topic!

Jul 1, 2015
Update: Elio Tracker has been voluntarily deactivated due to some unexpected effects it could have had regarding Regulation A+. This form of investment is new and comes with some unique issues that a public data tracker like this brought to light. Further updates on the progress of campaigns will not be issued by this tracker, but the tech works!

Elio Motors campaign progress bar

Get out your rulers.

Elio Motors is a company attempting to bring a fascinating little three-wheeled car into production, and while they’ve made some very impressive progress (covered nicely on their blog), these things take money. A lot of it. In an effort to raise a part of the funds necessary to make it to production on time, Elio Motors has made a Regulation A+ crowdfunding offering enabled by the JOBS Act, but what the users at the Elio Owners forum found is that the campaign page itself gives little to no indication of its progress. Only by backing out to the site hosting Elio’s campaign can you see could you have seen a progress bar, and it’s it was tiny. There’s no indication of dollar amount, and no indication of percent completed.

Now I do understand thought I understood the thinking behind not including those things (though the following information doesn’t adequately cover the reasoning). This is a Reg. A+ offering, so no money has been raised, and any dollar amount would be irrelevant because that number only translates (roughly) to “interest.” In other words, no matter what the dollar amount comes out to, Elio Motors may never see a penny of it if the SEC filing doesn’t go through, or if the interested investors become uninterested. Still though, I like data, and so do the people on the forums.

The key point here is that the progress bar is getting its width from a CSS property, and as I’ve covered in a previous post about YQL scraping, I can use Yahoo!’s YQL to pull a copy of the page’s HTML and then filter out everything but that property with increasingly specific regular expressions. Combined with a timestamp of when each scrape occurs and an educated guess as to how much Elio Motors is offering in total, I can estimate percentage complete and dollars raised at any given time. Plot that out on a chart and you’ve got a pretty good campaign tracker.

Elio Motors realtime crowdfunding tracking at eliotracker.com

To implement this as a realtime web application and limit the number of YQL queries I’d need to send, I went with a full-stack JavaScript strategy using Meteor. The server performs the scrape once every thirty minutes, then saves the percentage and timestamp as a record in a MongoDB collection that gets synced with the client. On the client side, a chart from highcharts plots a line using the data found in the collection, then uses label formatters to turn that data into human-readable information. In this way, all heavy lifting is performed by the client while the server focuses only on handling connections and occasionally doing a quick YQL scrape.

The resulting tracker is available until the end of the campaign at www.eliotracker.com.

What I’ve found is that it’s very entertaining to check in on the chart from time to time. There seem to be some general patterns to the times at which people show the most interest. The project should be especially interesting when Elio Motors gets additional publicity that could lead more potential investors to the campaign, such as the Reddit AMA on Thursday, July 2.

Dec 3, 2014

One point that became immediately clear upon running my Raspberry Pi remote control car was that any decent control system needed a way to accommodate new hardware and improved scripting without requiring significant, irreversable modifications to the program. The bot needed a way to accept easily upgradeable bite-size plugins to control things like moving forward […]

Continue reading...
Nov 23, 2014

As part of an ongoing series of improvements to a remote control car I bought at a thrift store, I needed a stream of GPS data from a Sierra Wireless Overdrive Pro 3G/4G hotspot (which, coincidentally, I also picked up at a thrift store). After registering the device with my carrier, logging into the admin […]

Continue reading...
Fork me on GitHub