Skip to content

Instantly share code, notes, and snippets.

@mwcz
Last active February 26, 2017 05:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwcz/5817982 to your computer and use it in GitHub Desktop.
Save mwcz/5817982 to your computer and use it in GitHub Desktop.
Michael Mathews (JSDoc founder) explains how @name works and how to document my IIFE-nested multiple-AMD module definitions (wow, that sounds cooooool).

#########

Me:

#########

It's not that I want to avoid those particular tags; I just want to generate the most documentation with the fewest number of tags. Lots of tags start impacting code readability.

Here's a better illustration of my use case. A few constructors are defined inside of an IIFE in order to keep them out of the global namespace. They are then exported with an AMD module definition at the bottom:

(function () {

    /**
     * @name Bananana
     * @constructor
     */
    function Bananana () {
        /**
         * @name czar
         * @memberof Bananana
         * @function
         * @instance
         */
        this.czar = function () {
            /* huhwhat? */
        };
    }

    /**
     * @name BoaConstrictor
     * @constructor
     */
    function BoaConstrictor () {
        /**
         * @name squeeze
         * @memberof BoaConstrictor
         * @function
         * @instance
         */
        this.squeeze = function () {
            /* oof */
        };
    }

    define({
        bananana        : Bananana,
        boa_constrictor : BoaConstrictor
    });

}());

I didn't see a use of @module or @exports that quite matched what I'm doing. Thanks again for your help. :]

######################

Michael Mathews:

######################

Hi Michael,

Okay, that looks more like a real use case.

Essentially you're asking about documenting AMD modules. That's actually quite straightforward to do with JSDoc (see http://usejsdoc.org/howto-commonjs-modules.html ). The twist that makes the job slightly hard is that you want to document modules that are also constructor functions, and you want to document more than one, in the same file and scope. Don't fear, we will get there.

Let's start with why you need so many tags to do it the way you tried in your example. It all comes down to that @name tag. This is a a tag with some major side effects. It should probably be called the @useThisNameAndIgnoreTheSurroundingCode tag. In fact it started out, in JSDoc 1, with the title @virtual, which might give you a better idea of the side effects: it treats the comment as if it were completely disconnected from the surrounding code. For example, the following two snips will generate the same documentation.

document a bar:

/** @function bar */
function foo () {
    this.baz = 1;
}

also document a bar:

/** @ function bar */

Surprised? You can actually delete the function from the source code and that comment has the exact same meaning. That's because the name of any symbol in your code is the key to every aspect of how JSDoc indexes and refers to it. If you say you are going to provide your own name, this switches JSDoc into a mode where it will completely ignore any code around that comment. This is a feature... really. Because it means, no matter how wacky your source code, even if your source code is literally a bunch of empty files, as long as you have comments with names, you can have some JSDoc documentation

So now I hope you see why, when you use @name, you suddenly have to also use @memberof, and also @function, and also @instance. You probably thought JSDoc was being stupid for not just seeing all those traits in your code, but what you probably didn't realise is that you just told it not to even look at that code (by using @name).

So, we have learned that, if you are trying to write less tags, then @name is going to take you in the wrong direction. Fast.

But what tags are better to use then? (I hear you ask.) Well, good news! JSDoc 3 introduces a less drastic version of @name, one that has the same effect of giving your code symbol a new name, but doesn't then all-out ignore your surrounding code: it's the @alias tag. I'll show you how to document your example code using your new friend.

(function () {

    /**
     * What is love?
     * @global
     * @alias module:Bananana
     * @constructor
     */
    function Bananana () {
        /** Czoom czoom. */
        this.czar = function () {
        };
    }

    /**
     * Give me a hug.
     * @global
     * @alias module:BoaConstrictor
     * @constructor
     */
    function BoaConstrictor () {
        /** Squish squish. */
        this.squeeze = function () {
        };
    }

    define({
        bananana        : Bananana,
        boa_constrictor : BoaConstrictor
    });

}());

This is a minor improvement. You still have to use the @global tag, because JSDoc can't know that later on you will be exporting these constructors. I think this is the best you'll be able to do with the code as it is written. But JSDoc is actually optimised for a slightly different code pattern, and if you are able to follow that pattern, you can shed a few more tags. You example again, slightly rearranged:

define('bananana',
/** @exports bananana */
function () {
    /**
     * What is love?
     * @constructor
     */
    var exports = function() {
        /** Czoom czoom. */
        this.czar = function () {
        };
    }
    return exports;
});

define('boa_constrictor',
/** @exports boa_constrictor */
function () {
    /**
     *  Give me a hug.
     *  @constructor
     */
    var exports = function() {
        /** Squish squish. */
        this.squeeze = function () {
        };
    }
    return exports;
});

I think this is the best we can do. But if you're stuck with code in a different format you can always try @alias. And if your code is completely out in left field, there's always @name but use it with caution, it's strong medicine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment