JSDoc should do more to interpret IIFEs #456

Open
rvernica opened this Issue Jul 16, 2013 · 12 comments

Projects

None yet

5 participants

@rvernica

JSDoc does not detect functions created by compiling CoffeeScript functions with proper JSDoc comments. Here is a simple example:

x = 1

###*
   * Funtion to ...
###
a = () ->
  1

x = 1 or some other code had to be before the comment, otherwise CoffeeScript assigns the comment to the file outer anonymous function and not the inner function.

After compilation CoffeeScript generates the following JavaScript code:

// Generated by CoffeeScript 1.6.3
(function() {
  var a, x;

  x = 1;

  /**
     * Funtion to ...
  */


  a = function() {
    return 1;
  };

}).call(this);

On this code, JSDoc does not detect the a function.

If the var a, x; line is removed, JSDoc works fine.

@hegemonic
Member

CoffeeScript, and most other compile-to-JS languages, can slice and dice your code in a variety of ways. Inevitably, some of that slicing and dicing will defeat JSDoc's static-analysis tools.

That said, if JSDoc did a better job of interpreting immediately invoked function expressions, such as the (function() {}).call(this) wrapper, it would be able to handle this situation correctly. I'll use this issue as a feature request for that change (and update the summary accordingly).

Removing the var a, x; line is not a good solution here, since it just turns a and x into globals.

@rvernica

That makes sense. Thanks!

@KyleJSummers

Is there currently any way to effectively document apps with JSDoc that use IIFE's to encapsulate code?

@hegemonic
Member

Yes, you can document code that uses IIFEs--it just requires a bit more markup. Our documentation website has some examples for documenting AMD modules that should help you get started.

We could use some non-module examples in the docs as well; I filed jsdoc3/jsdoc3.github.com#41 to address that at some point.

@hegemonic
Member

Another common use case, from #442: (function() {})(window);

Before resolving this issue, we may need to tackle the more general issue that JSDoc doesn't recognize when two symbols are effectively sharing a pointer. For example, if you're documenting namespace X, and your code says var Y = X, JSDoc won't recognize that the method Y.getFoo can be documented as X.getFoo. (We don't do this because it's hard and creates a lot of edge cases, but at some point we could.)

@boba1l0s2k9

I created a hackaround using part of the JSDoc plugin framework to filter out IIFE's before JSDoc sees the code, see jsdoc-plugin-strip-outer-iife. You can see the source code is pretty straight-forward. I believe it handles all the cases mentioned in this thread. But most importantly it handles my cases perfectly. :) This isn't the right solution for this problem, but since it works for me I thought I'd share.

@hegemonic
Member

Another common use case that I allude to in a previous comment: Adding properties to window, or some other symbol that's defined outside of the IIFE's scope. (This came up again in #614. I don't think we currently infer @global when a property is added to window, although that would be useful. We might want to make this opt-in or implement it as a plugin.)

@hegemonic hegemonic added this to the 3.4.0 milestone Oct 11, 2014
@ejfrancis

I was going to create a separate issue but since my problem is related to IIFEs and this issue is titled "do more to interpret IIFEs" I guess my comment fits here. I'm using an IIFE in a CommonJS module to be able assign the public interface to either "module.exports" or the "window" object, so that a node.js module can be included in a <script> tag in a browser environment and it works there as well. Here is the example code

/**
 * This is a description
 *
 *  @module someModule
 */

(function (exports) {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    exports.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);

This works as expected because the exports object is passed into the IIFE.

This example doesn't work, however. It looks like the exports object passed into the IIFE will only be recognized by JSDoc if it's actually named "exports". JSDoc doesn't document the public interface if it's attached to the object passed into the IIFE and that object is NOT named "exports", even the the "ex" argument is just the module.exports object being passed in. I'll show that in this example, which doesn't work

/**
 * This is a description
 *
 *  @module someModule
 */

(function (ex) {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    ex.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);

This example does work though, when the exports object isn't passed into the IIFE at all

/**
 * This is a description
 *
 *  @module someModule
 */

(function () {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    exports.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);
@hegemonic
Member

@ejfrancis This example in the docs shows how to document your second example.

@ejfrancis

@hegemonic I've tried that and can't get it to work. Those docs are for AMD and I'm using it within CommonJs. I couldn't tell if using the @exports tag still required the @module tag so I tried these three permutations and none worked

A with @module and @exports using different names

/**
 * This is a description
 *
 *  @module someModule
 */

(function (
    /**
     * Utility functions for jackets.
     * @exports someModuleExports
     */
    ex) {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    ex.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);

B with @module and @exports using the same name

/**
 * This is a description
 *
 *  @module someModule
 */

(function (
    /**
     * Utility functions for jackets.
     * @exports someModule
     */
    ex) {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    ex.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);

C with no @module tag

(function (
    /**
     * Utility functions for jackets.
     * @exports someModule
     */
    ex) {

    /**
     *  A method
     * @returns {Boolean}
     * @static
     */
    ex.isSomething = function() {
        return true;
    };


})(
    typeof exports === 'undefined'? window: exports
);
@hegemonic
Member

C with no @module tag looks perfect to me. What part of the output are you unhappy with?

@ejfrancis

@hegemonic the docs aren't even generated for the CommonJS module at all with C. without the @module tag it's just the README file that's generated into the index.html file. With A and B it generates the page for the module, but only lists the module name and description, doesn't have docs for any public methods/properties. I just tried each again in an empty project with only the module file and a README and confirm this behavior

@hegemonic hegemonic modified the milestone: Future, 3.4.0 Oct 22, 2015
@75lb 75lb referenced this issue in jsdoc2md/jsdoc-to-markdown Dec 6, 2015
Closed

Docs missing inside the IIFE #29

@openstack-gerrit openstack-gerrit pushed a commit to openstack-infra/js-generator-openstack that referenced this issue Jul 12, 2016
@yujunz yujunz Remove IIFE in all scripts
It is not required by node.js, but introduce troubles while
generating documents with jsdoc3.

Refer to jsdoc3/jsdoc#456

Change-Id: I6664f1be0fc3e86f3a7e3843d557786c37df1c5e
5c9e5bb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment