Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save belachkar/56ba598e48b6a98f0784b5c28ea774c2 to your computer and use it in GitHub Desktop.
Save belachkar/56ba598e48b6a98f0784b5c28ea774c2 to your computer and use it in GitHub Desktop.
JavaScript: JSDoc Advanced Tips
/* https://leahayes.wordpress.com/2011/08/28/documenting-javascript-with-jsdoc3/
Namespaces can still be documented when a more abstract mechanism is used. @lends allows members to be added to an existing namespace:
*/
/**
* Root namespace
* @namespace root
*/
$namespace('root', /** @lends root **/ {
/**
* Do something really neat
*/
foo: function() {
}
});
/**
* Nested namespace
* @namespace root.nested
*/
$namespace('root.nested', /** @lends root.nested **/ {
/**
* Do something else that is really neat
*/
bar: function() {
}
});
/*
https://stackoverflow.com/questions/19230971/how-do-i-jsdoc-a-nested-objects-methods
You can't document nested functions directly. I didn't like Prongs solution, so I used a different implementation without namespaces (it's JS, not Java!).
*/
/** @module foobar */
/** @function */
function foobarbaz() {
/*
* You can't document properties inside a function as members, like you
* can for classes. In Javascript, functions are first-class objects. The
* workaround is to make it a @memberof it's closest parent (the module).
* manually linking it to the function using (see: {@link ...}), and giving
* it a @name.
*/
/**
* Foo object (see: {@link module:foobar~foobarbaz})
* @name foo
* @inner
* @private
* @memberof module:foobar
* @property {Object} foo - The foo object
* @property {Object} foo.bar - The bar object
* @property {function} foo.bar.baz - The baz function
*/
var foo = {
/*
* You can follow the same steps that was done for foo, with bar. Or if the
* @property description of foo.bar is enough, leave this alone.
*/
bar: {
/*
* Like the limitation with the foo object, you can only document members
* of @classes. Here I used the same technique as foo, except with baz.
*/
/**
* Baz function (see: {@link module:foobar~foo})
* @function
* @memberof module:foobar
* @returns {string} Some string
*/
baz: function() { /*...*/ }
}
};
return foo;
}
/** @class */
function Hello() {
/** @member {Object} */
var hi = {};
}
/*
Just to improve on Prongs's answer a bit for JSDoc3, I was only able to get it to work when I used the @instance annotation in lieu of @member.
ES6 example code follows:
*/
class Test
{
/**
* @param {object} something
*/
constructor(something)
{
this.somethingElse = something;
/**
* This sub-object contains all sub-class functionality.
*
* @type {object}
*/
this.topology = {
/**
* Informative comment here!
*
* @alias topology.toJSON
* @memberof! Test#
* @instance topology.toJSON
*
* @returns {object} JSON object
*/
toJSON()
{
return deepclone(privatesMap.get(this).innerJSON);
},
}
}
}
/*
https://leahayes.wordpress.com/2011/08/28/documenting-javascript-with-jsdoc3/
Class (with prototype object)
*/
/**
* Base class of a game entity
* @class Entity
*/
Entity = function() {
};
/**
* Render entity
* @param {CanvasRenderingContext2D} dc Device context
*/
Entity.prototype.render = function(dc) {
// Instance method!
};
/**
* Get new instance of entity
* @param {string} type Type of entity to instantiate
* @returns {@link Entity}
*/
Entity.getInstance: function(type) {
// Static method!
};
/*
https://leahayes.wordpress.com/2011/08/28/documenting-javascript-with-jsdoc3/
Classes (with classical approach)
*/
/**
* Base class of a game entity
* @class Entity
*/
$class('Entity', /** @lends Entity# **/ {
/**
* Render entity
* @param {CanvasRenderingContext2D} dc Device context
*/
render: function(dc) {
// Instance method!
}
}, /** @lends Entity **/ {
/**
* Get new instance of entity
* @param {string} type Type of entity to instantiate
* @returns {@link Entity}
*/
getInstance: function(type) {
// Static method!
}
});
/**
* Ball entity
* @class Ball
* @extends Entity
*/
$class('Ball', 'Entity', /** @lends Ball# **/ {
/**
* Force ball to bounce
*/
bounce: function() {
// Instance method!
}
});
/*
https://groups.google.com/forum/#!topic/jsdoc-users/izErstbwNUU
I advocate the use of already-defined patterns.
A bunch of new tags is not the main aim of my proposition.
Please find below JS code with comments.
It is self descriptive example.
-- Tawez
*/
/**
* @namespace
*/
Ns = {
/**
* Nested structures could be documented in simple way,
* we just need to reuse already defined patterns.
* Property dot notation (http://code.google.com/p/jsdoc-toolkit/wiki/
TagParam#Parameters_With_Properties) works,
* but output is flat and we lose information about property
structure.
* Michael asked about view. IMHO it should be rendered as in
following example (simplified markdown like syntax to illustrate the
idea):
* @example
* - Object staticDeepStruct
* Comments etc.
* - Number i
* Positive val
* - String s
* String var
* @type Object
*/
staticDeepStruct: {
/**
* Positive val
* @type Number
* @default 1
*/
i: 1,
/**
* String var
* @type Number
* @default "text"
*/
s: "text",
}
};
/*
-----------------------------------------------------------------------
* This comment block should give following documentation for
constructor:
* Class Detail
* Ns.Base(config)
* +-------+
* |example|
* +-------+
* +-------+
* |example|
* +-------+
*
* Parameters:
* {Object} config Optional
* Config object
* - {String} text
* Value for text property
*
-----------------------------------------------------------------------
*/
/**
* @class
* @example
* var b = new Ns.Base();
* @example
* var b = new Ns.Base({
* text: "new value"
* });
* @param {Object} [config] Config object
* @param {String} config.text Value for text property
*/
Ns.Base = createClass(Object, /** @lends Ns.Base.prototype */ {
/**
* @type String
* @default "make some noise"
*/
text: "make some noise",
/**
* deepStruct should be rendered in the same way as
Ns.staticDeepStruct
* @type Object
*/
deepStruct: {
/**
* Positive val
* @type Number
* @default 111
*/
i: 111,
/**
* String var
* @type Number
* @default "more text"
*/
s: "more text",
},
/**
* Returns text property value
* @returns {String}
*/
foo: function () {
return this.text;
}
/**
* If we have "directive" lends, then tag memberOf is unnecessary
here - we have well defined "context"
* @function
* @name bar
* @memberOf Ns.Base.prototype
* @returns Number
*/
});
/**
* @class
* @augments Ns.Base
* @property {Number} [i=1]
* Some comments to i property
* @example
* // this is an example for i property,
* // but will be parsed and rendered as constructor example, arghhhh!
* @property {String} [s=""]
* Some comments to s property
*/
Ns.Derived = createClass(Ns.Base, /** @lends Ns.Derived.prototype */ {
/**
* As far as I know this is the only working pattern
* if I'd like to redefine property documentation with a lot of text
and examples
* and without providing the code of property.
* BTW If we have "directive" lends, then tag fieldOf is unnecessary
here - we have well defined "context"
* @example
* // this is an example for text property,
* // and will be rendered in the right place
* @name text
* @fieldOf Ns.Derived.prototype
* @type String
* @default "be quiet"
*/
/**
* Dot notation works but right now function gets wrong definition as
<tt>{Object<u>, number,
number</u>} foo()</tt> (I think it is technical problem only).
* As an alternative we can use colon notation as below.
* Should be rendered in the same way as Ns.staticDeepStruct
* @returns {Object} The coordinates of the current target
* @returns {number} return:x The x value.
* @returns {number} return:y The y value.
*/
foo: function () {
return {
x: 0,
y: 0
};
}
});
// JSDoc the object. Then its members.
/**
* @param data Information about the object.
* @param data.member Information about the object's members.
*/
function getData(data){
console.log(data.member);
}
/*
Tested in PyCharm. @Nicholi confirms it works in Webstorm.
The {{ member:type }} syntax Andreas suggested may conflict with Django templates.
Thanks to Jonny Buchanan's answer citing the @param wiki.
To document arrays of objects, use [] brackets as JSDoc suggests:
*/
/**
* @param data
* @param data.array_member[].foo
*/
/**
* @param {{some_unres_var:string}} data
*/
function getData(data){
console.log(data.some_unres_var);
}
/*
All other answers are incorrect for the general case. What if you don't get data as a parameter? You don't have JSDoc then:
*/
function niceApiCall(parameters) {
const result = await ... // HTTP call to the API here
for (const e of result.entries) {
.. // decorate each entry in the result
}
return result;
}
/*
WebStorm will warn that "result.entries" is an unresolved variable (field).
The general solution is to add an @namespace declaration:
*/
function niceApiCall(parameters) {
/** @namespace result.entries **/
const result = await ... // HTTP call to the API here
for (const e of result.entries) {
.. // decorate each entry in the result
}
return result;
}
// An object with string keys and number values:
{Object.<string, number>}
// An array of MyClass instances.
{Array.<MyClass>}
// or:
{MyClass[]}
// This can be a number or a boolean.
{(number|boolean)}
// Specifies the name of a symbol. If you have documented the symbol, JSDoc creates a link to the documentation for that symbol.
{boolean}
{myNamespace.MyClass}
// A number or null.
{?number}
// A number, but never null.
{!number}
// This function accepts a variable number of numeric parameters.
@param {...number} num
// An optional parameter named foo.
@param {number} [foo]
// or:
@param {number=} foo
// An optional parameter foo with default value 1.
@param {number} [foo=1]
/**
@callback myCallback
@param {number} x - ...
*/
/* @type {myCallback} */
var cb;
//Type Def
// Documenting a type with properties 'id', 'name', 'age'.
/**
@typedef PropertiesHash
@type {object}
@property {string} id - an ID.
@property {string} name - your name.
@property {number} age - your age.
/
/** @type {PropertiesHash} /
var props;
/*
In this example we have a namespace named "config." We want all the information about the defaults property, including its nested values, to appear on the same page with the documentation for config.
A namespace with defaults and nested default properties
*/
/**
* @namespace
* @property {object} defaults - The default values for parties.
* @property {number} defaults.players - The default number of players.
* @property {string} defaults.level - The default level for the party.
* @property {object} defaults.treasure - The default treasure.
* @property {number} defaults.treasure.gold - How much gold the party starts with.
*/
var config = {
defaults: {
players: 1,
level: 'beginner',
treasure: {
gold: 0
}
}
};
// A type definition with required and optional property
/**
* User type definition
* @typedef {Object} User
* @property {string} email
* @property {string} [nickName]
*/
/**
* @param {string} key
* @param {string} value
* @param {string} [expires='']
* @param {string} [sameSite='/']
* @param {string} [domain] domain (e.g., 'example.com' or 'subdomain.example.com').
* If not specified, defaults to the host portion of the current document location.
* If a domain is specified, subdomains are always included.
* Domain must match the domain of the JavaScript origin. Setting cookies to foreign domains will be silently ignored.
*/
export function setCookie(key, value, expires, sameSite, domain) {
if (hasDeviceAccess()) {
document.cookie = `${key}=${encodeURIComponent(value)}${(expires !== '') ? `; expires=${expires}` : ''}; path=/${sameSite ? `; SameSite=${sameSite}` : ''}${domain ? `; domain=${domain}` : ''}`;
}
}
/*
https://groups.google.com/forum/#!topic/jsdoc-users/izErstbwNUU
I've updated the issue tracker with the work I've done on this so far.
https://github.com/micmath/jsdoc/issues/34
This unfortunately entails an API change, but the work is currently in
a separate branch. The summary is that if you are using JSDoc 3 and
are currently using the `@property` tag, you should change those to
`@member` tags. This can be a simple search & replace change. I would
expect this to impact only a very few people, since JSDoc 3 is still
in beta and the `@property` tag is not actually documented yet in that
version.
Note that this change does not have any affect on JSDoc Toolkit
whatsoever. In fact this should make JSDoc 3 more compatible with
`@property` tags as used in JSDoc Toolkit.
I expect I will merge this change into the master branch in the next
week or so.
*/
/*
https://groups.google.com/forum/#!topic/jsdoc-users/izErstbwNUU
I think how you present the results depends on how it is being used. If
the components of the object are intended to be consumed or set whole
(in one step or scope), I think the entire object should be on one page.
If the fields are set one at a time, or one sub-object at a time, then
separate pages, one per container, would probably be preferable.
But, that can't be gleaned from the code. I think the tag has to tell
the template how to render it by classifying the types and assigning a
tag for each, or to repurpose @kind or another similar tag.
For instance, if we consider the first type, where it is all consumed
and modified at once a "container" and the other type, which is handled
piecemeal, a "hash", then you could document it as follows:
*/
/** @container taskContainer */
myobject = {
/** @hash defaults (would document as taskContainer.defaults */
defaults: {
/** @hash a */
a: {
/** @hash aa */
aa: {
/** when true, your hair will fall out */
aaa: true
},
/** returns an object (would you document the return here? or on the return line?) */
aa2: function(options){
return {
aaa2: false
};
}
},
/** blah (no need to decorate further, rhino will tell us this is child of taskContainer)
*/
b: "Hit the light",
/** blah
*/
c: true
}
};
/*
Of course, the terms "hash" and "container" were just used to
illustrate the concept. Hash is definitely not the right term, but
container might work long term.
When presenting the template, just follow the logic, each container will
contain all contents that are not hashes, classes, namespaces, etc on
one page. Each hash will gets its own page and link from the documented
member's attribute, giving it a type of the named @hash and a link to
the docs for the hash. It leaves the programmer in control of how to
navigate the symbols and that may make the ultimate solution to this
problem simpler.
It does, however, pollute the tag namespace. And I don't think it solves
for documenting returned containers easily.
*/
// https://stackoverflow.com/questions/17119100/how-to-use-jsdoc3-to-document-nested-namespaces
/**
* @namespace MyNamespace.MySubNamespace
*/
(function (MyNamespace) {
/**
* Foo namespace
* @namespace Foo
* @memberOf MyNamespace.MySubNamespace
*/
var Foo = {
/**
* Does something.
* @memberOf MyNamespace.MySubNamespace.Foo
* @param {object} someParam Some parameter.
*/
doSomething: function (someParam) {
// doing it
}
};
MyNamespace.MySubNamespace.Foo = Foo;
})(window.MyNamespace)
/*
https://stackoverflow.com/questions/19230971/how-do-i-jsdoc-a-nested-objects-methods
You can use a combination of @module or @namespace along with @memberof.
*/
define([], function() {
/**
* A test module foo
* @version 1.0
* @exports mystuff/foo
* @namespace foo
*/
var foo = {
/**
* A method in first level, just for test
* @memberof foo
* @method testFirstLvl
*/
testFirstLvl: function(msg) {},
/**
* Test child object with child namespace
* @memberof foo
* @type {object}
* @namespace foo.bar
*/
bar: {
/**
* A Test Inner method in child namespace
* @memberof foo.bar
* @method baz
*/
baz: function() { /*...*/ }
},
/**
* Test child object without namespace
* @memberof foo
* @type {object}
* @property {method} baz2 A child method as property defination
*/
bar2: {
/**
* A Test Inner method
* @memberof foo.bar2
* @method baz2
*/
baz2: function() { /*...*/ }
},
/**
* Test child object with namespace and property def.
* @memberof foo
* @type {object}
* @namespace foo.bar3
* @property {method} baz3 A child method as property defination
*/
bar3: {
/**
* A Test Inner method in child namespace
* @memberof foo.bar3
* @method baz3
*/
baz3: function() { /*...*/ }
},
/**
* Test child object
* @memberof foo
* @type {object}
* @property {method} baz4 A child method
*/
bar4: {
/**
* The @alias and @memberof! tags force JSDoc to document the
* property as `bar4.baz4` (rather than `baz4`) and to be a member of
* `Data#`. You can link to the property as {@link foo#bar4.baz4}.
* @alias bar4.baz4
* @memberof! foo#
* @method bar4.baz4
*/
baz4: function() { /*...*/ }
}
};
return foo;
});
/*
EDIT as per Comment: (Single page solution for module)
bar4 without that ugly property table. ie @property removed from bar4.
*/
define([], function() {
/**
* A test module foo
* @version 1.0
* @exports mystuff/foo
* @namespace foo
*/
var foo = {
/**
* A method in first level, just for test
* @memberof foo
* @method testFirstLvl
*/
testFirstLvl: function(msg) {},
/**
* Test child object
* @memberof foo
* @type {object}
*/
bar4: {
/**
* The @alias and @memberof! tags force JSDoc to document the
* property as `bar4.baz4` (rather than `baz4`) and to be a member of
* `Data#`. You can link to the property as {@link foo#bar4.baz4}.
* @alias bar4.baz4
* @memberof! foo#
* @method bar4.baz4
*/
baz4: function() { /*...*/ },
/**
* @memberof! for a memeber
* @alias bar4.test
* @memberof! foo#
* @member bar4.test
*/
test : true
}
};
return foo;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment