fix exports tag when the module object is passed to an AMD function (#642)

- Do not mark the doclet for the module object as undocumented.
- Track variables (including aliases) within the parent scope, so the members are resolved against the alias instead of the name used in the code.
This commit is contained in:
Jeff Williams 2014-09-19 09:01:14 -07:00
parent 71de4acaf0
commit 73998951b1
3 changed files with 67 additions and 4 deletions

View File

@ -69,6 +69,12 @@ function makeInlineParamsFinisher(parser) {
return;
}
// we only want to use the doclet if it's param-specific (but not, for example, if it's
// a param tagged with `@exports` in an AMD module)
if (e.doclet.kind !== 'param') {
return;
}
parentDoclet.params = parentDoclet.params || [];
documentedParams = parentDoclet.params;
knownParams = parentDoclet.meta.code.paramnames;
@ -76,9 +82,8 @@ function makeInlineParamsFinisher(parser) {
while (true) {
param = documentedParams[i];
// is the param already documented? if so, we're done
// is the param already documented? if so, we don't need to use the doclet
if (param && param.name === e.doclet.name) {
// the doclet is no longer needed
e.doclet.undocumented = true;
break;
}
@ -351,20 +356,23 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) {
break;
// like "bar" in: function foo(/** @type {string} */ bar) {}
// or "module" in: define("MyModule", function(/** @exports MyModule */ module) {}
// This is an extremely common type of node; we only care about function parameters with
// inline type annotations. No need to fire events unless they're already commented.
// inline comments. No need to fire an event unless the node is already commented.
case Syntax.Identifier:
parent = node.parent;
if ( node.leadingComments && parent && jsdoc.src.astnode.isFunction(parent) ) {
extras.finishers = [makeInlineParamsFinisher(parser)];
e = new SymbolFound(node, filename, extras);
trackVars(parser, node, e);
}
break;
// like "obj.prop" in: /** @typedef {string} */ obj.prop;
// Closure Compiler uses this pattern extensively for enums.
// No need to fire events for them unless they're already commented.
// No need to fire an event unless the node is already commented.
case Syntax.MemberExpression:
if (node.leadingComments) {
e = new SymbolFound(node, filename, extras);

18
test/fixtures/exportstag6.js vendored Normal file
View File

@ -0,0 +1,18 @@
define(function(
/**
* A module representing a shirt.
* @exports my/shirt
* @version 1.0
*/
shirtModule) {
/** A property of the module. */
shirtModule.color = 'black';
/** @constructor */
shirtModule.Turtleneck = function(size) {
/** A property of the class. */
this.size = size;
};
return shirtModule;
});

View File

@ -13,6 +13,7 @@ describe('@exports tag', function() {
it('When an objlit symbol has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() {
expect(typeof shirt).toEqual('object');
expect(shirt.alias).toEqual('my/shirt');
expect(shirt.undocumented).not.toBeDefined();
});
it('When an objlit symbol has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() {
@ -28,7 +29,10 @@ describe('@exports tag', function() {
expect(color.memberof).toEqual('module:my/shirt');
expect(typeof tneck).toEqual('object');
expect(tneck.memberof).toEqual('module:my/shirt');
expect(typeof size).toEqual('object');
expect(size.memberof).toEqual('module:my/shirt.Turtleneck');
});
});
@ -96,4 +100,37 @@ describe('@exports tag', function() {
expect(method.description).toBe('This should be in the Foo module doc.');
});
});
describe("'exports' object as a parameter to 'define'", function() {
var docSet = jasmine.getDocSetFromFile('test/fixtures/exportstag6.js');
var shirt = docSet.getByLongname('module:my/shirt')[0];
var color = docSet.getByLongname('module:my/shirt.color')[0];
var tneck = docSet.getByLongname('module:my/shirt.Turtleneck')[0];
var size = docSet.getByLongname('module:my/shirt.Turtleneck#size')[0];
it('When a param has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() {
expect(typeof shirt).toBe('object');
expect(shirt.alias).toBe('my/shirt');
expect(shirt.undocumented).not.toBeDefined();
});
it('When a param has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() {
expect(shirt.longname).toBe('module:my/shirt');
});
it('When a param has an @exports tag, the doclet kind is set to module.', function() {
expect(shirt.kind).toEqual('module');
});
it('When a param has an @exports tag, the properties added to the param are documented as members of the module.', function() {
expect(typeof color).toBe('object');
expect(color.memberof).toBe('module:my/shirt');
expect(typeof tneck).toBe('object');
expect(tneck.memberof).toBe('module:my/shirt');
expect(typeof size).toBe('object');
expect(size.memberof).toBe('module:my/shirt.Turtleneck');
});
});
});