Nested Methods in sidebar of JSDoc

2.8k views Asked by At

Thanks to the answer found here:

https://stackoverflow.com/a/19336366/592495

My JavaScript documentation is well-organized and well-formatted. Each namespace is a "parent" of methods contained within. However, navigation is not quite as granular as I would like.

After compiling/rendering using the node.js tool via a simple command (jsdoc file1.js file2.js), the docs are generated into a default template. This default template displays my namespaces in the sidebar navigation, but it does NOT show methods that each contains.

You can fake a list of methods by adding the @class directive to each method, but as we know, they are not really classes.

I would love to see a sidebar navigation like this:

My Project

 - namespace 1
    - method.a
    - method.b
    - method.c

 -namespace 2
    - method.d
    - method.e

Any direction to documentation I have overlooked would be greatly appreciated.


[edit to add:]

Upon experimentation, @class does almost exactly what I want but with some exceptions:

  • It lists classes above namespaces. I don't like that since the namespaces are the "parents" as it were.

  • JavaScript doesn't have classes in that sense. Not ones that are called "classes" with that nomenclature. It creates a weird disconnect when reading the document to see a list of "classes".

  • It adds the "new" operator automagically. Not all of the methods have constructors... you can see the problem!


[edit: sample code]

So here's the current structure. Before I annotate it with JSDoc comments, here's the basic approach:

var app =  app || {};
app.utils = {
    whizbang: function() {},
    geegolly: function() {}
  };
app.render = {
    thestuff: function(params) {},
    thethings: function(params) {}
  }
}

So, using object literal notation, the top level is a "namespace" for the whole application, but within there are sub-namespaces for different purposes. Here, I have a sub-namespace specific to utilities, and another one specific to rendering. Each can have properties, but more importantly they each contain functions. It is these functions which should appear in the sidebar. Now to flesh it out with my current pattern for JSDoc:

/** 
 * @file MyApp.js This is an awesome description of MyApp.js
 * 
 * @version 0.1
 * @author Greg Pettit
 * @copyright 2015
 * 
 */

/**
 * Description of my main namespace!
 * 
 * @namespace app
 */
var app = app || {};

/**
 * This is a description of my sweet utilities namespace!
 *                                                                              
 * @memberof app
 * @type {object}
 * @namespace app.utils
 */
app.utils = {
  /**
   * app.utils.whizbang is an awesome function with magical powers. I sure wish
   * it would appear in the sidebar as a member of app.utils!
   * 
   * @memberof app.utils
   * @method whizbang
   * 
   * @param {method} [successCallback] Method invoked on successful attempt.
   * @param {method} [errorCallback] Method invoked on unsuccessful attempt.
   * 
   */
   whizbang: function(successCallback, errorCallback) { // do utility stuff! }
}

/**
 * This is a description of the best rendering namespace ever.
 *                                                                              
 * @memberof app
 * @type {object}
 * @namespace app.render
 */
app.render = {
  /**
   * app.render.thethings renders the things! I wish it would render to the sidebar...
   * 
   * @memberof app.render
   * @method thethings
   * 
   * @param {method} node The node to which thethings are rendered
   * 
   */
   thethings: function(node) { // do rendering stuff! }
}
1

There are 1 answers

4
Isochronous On

Have you tried using the @lends tag? An example of your code and doc comments would be helpful here.

Since I don't know what your code looks like, I'll just give an example of how I use JSDoc with our in-house framework, which has lots of idiosyncracies (hey, I didn't write it, I just have to use it).

Just to give some context, we have a context object that can create apps and modules (apps are just modules with a start method):

/** @namespace MyApp */
var MyApp = context.app('myApp').use('module1', 'module2', 'underscore');

We have a dependency injection system for backbone that uses an angular-style pattern for expressing dependencies:

/** 
* The constructor for MyModel
* @memberof MyApp
* @param {object?} attrs
* @param {object?} options
* @param {AppUtils} appUtils
* @constructor
*/  
MyApp.MyModel = function(attrs, options, appUtils) {
    this.options = options;
    this.utils = appUtils;
}

// This is injected by the dependency resolver at instantiation time
// No additional JSDoc comments are necessary, it actually gets this right
MyApp.MyModel.prototype = {

    idAttribute: 'customId',

    defaults: {
        customId: '',
        name: '',
        birthday: null
    }

};

// Registers MyModel with the IOC container as 'myModelName'
MyApp.model('myModelName', [
    'attrs',
    'options',
    'appUtils'
    MyApp.MyModel
]);

And then a different file can have an instance of myModelName injected by adding it to that dependency array at the bottom.

Funny thing is, JSDoc actually does a pretty good job of understanding that particular arrangement, as long as I don't try to get too fancy... but the following pattern is apparently too confusing for it:

/**
 * @memberof MyApp
 * @param {MyApp.MyModel} myModel
 * @param {urlParser} urlParser
 * @constructor
 */
MyApp.SomeService = function(myModel, urlParser) {

    return {

        foo: function() {
            //whatever
        },

        bar: function() {
            //whatever
        }

    };

};

MyApp.service('someModuleName', [
    'myModelName',
    'urlParser',
    MyApp.SomeService
]);

The only thing that I've found that gives me anything close to the desired output is using the @lends tag to tell JSDoc that a particular object/property/method is "lended" as a different property. For example, to document the attributes property of a backbone model (which is ostensibly defined by its defaults property), we do this:

MyApp.MyModel.prototype = {

    idAttribute: 'customId',

    /** @lends MyApp.MyModel.prototype.attributes */
    defaults: {
        customId: '',
        name: '',
        birthday: null
    }

};

And for that case where the service is returning an object, the only way we've found to get those object properties documented is like this:

/**
 * @memberof MyApp
 * @param {MyApp.MyModel} myModel
 * @param {urlParser} urlParser
 * @constructor
 */
MyApp.SomeService = function(myModel, urlParser) {

    /** @lends  MyApp.SomeService.prototype */
    return {

        foo: function() {
            //whatever
        },

        bar: function() {
            //whatever
        }
    };

};

I have no idea if any of that was useful, but maybe it'll give you some ideas for things you could try with @lends. If you can provide some example code, I can possibly give you a more useful answer.