diff --git a/examples/base.js b/examples/base.js index aa99226a..77c06226 100644 --- a/examples/base.js +++ b/examples/base.js @@ -10,7 +10,7 @@ var Animal = Class.extend({ /** * An instance property. - * @property {string} Animal#name + * @property {string|undefined} Animal#name */ this.name = name; }, diff --git a/modules/jsdoc/doclet.js b/modules/jsdoc/doclet.js index bdccef7d..58fe9a44 100644 --- a/modules/jsdoc/doclet.js +++ b/modules/jsdoc/doclet.js @@ -197,7 +197,7 @@ if (tagValue) { if (typeof o[tagName] === 'undefined') { // not defined - o[tagName] = tagValue; + o[tagName] = tagAbout.forceArray? [tagValue] : tagValue; } else if (typeof o[tagName].push === 'function') { // is an array o[tagName].push(tagValue); diff --git a/modules/jsdoc/schema.js b/modules/jsdoc/schema.js index 497278e4..8896bcc4 100644 --- a/modules/jsdoc/schema.js +++ b/modules/jsdoc/schema.js @@ -7,12 +7,12 @@ exports.jsdocSchema = { "properties": { - "docnode": { + "doc": { "type": "array", "items": { "type": "object", "properties": { - "path": { // unique to each documentation + "path": { // unique identifier for each doc "type": "string", "maxItems": 1 }, @@ -56,7 +56,7 @@ exports.jsdocSchema = { "optional": true, }, "type": { // what type is the value that this doc is associated with, like "number" - "type": "array", + "type": ["string", "array"], "optional": true, "items": { "type": "string" @@ -99,45 +99,45 @@ exports.jsdocSchema = { } } }, - "meta": { // information about this documentation + "tag": { // arbitrary tags associated with this doc + "type": "array", + "optional": true, + "items": { + "type": "object", + "properties": { + "name": { // the name of a tag + "type": "string" + }, + "desc": { // a value associated with that tag name + "type": "string", + "optional": true + } + } + } + }, + "meta": { // information about this doc "type": "object", "optional": true, "maxItems": 1, - "file": { // what is the name of the file this documentation appears in? + "file": { // what is the name of the file this doc appears in? "type": "string", "optional": true, "maxItems": 1 }, - "line": { // on what line of the file does this documentation appear? + "line": { // on what line of the file does this doc appear? "type": "number", "optional": true, "maxItems": 1 - }, - "tags": { // arbitrary tags associated with this documentation - "type": "array", - "optional": true, - "items": { - "type": "object", - "properties": { - "tagname": { // the name of a tag - "type": "string" - }, - "tagtext": { // a value associated with that tag name - "type": "string", - "optional": true - } - } - } } } } } }, - "meta": { // information about the generation for all the documentation + "meta": { // information about the generation for all the docs "type": "object", "optional": true, "maxItems": 1, - "project": { // to what project does this documentation belong + "project": { // to what project does this doc belong "type": "object", "optional": true, "maxItems": 1, @@ -151,17 +151,17 @@ exports.jsdocSchema = { "format": "uri" } }, - "generated": { // some information about the running of the documentation generator + "generated": { // some information about the running of the doc generator "type": "object", "optional": true, "maxItems": 1, - "date": { // on what date and time was the documentation generated? + "date": { // on what date and time was the doc generated? "type": "string", "maxItems": 1, "optional": true, "format": "date-time" }, - "parser": { // what tool was used to generate the documentation + "parser": { // what tool was used to generate the doc "type": "string", "maxItems": 1, "optional": true diff --git a/modules/jsdoc/tagdictionary.js b/modules/jsdoc/tagdictionary.js index 714677be..b3651966 100644 --- a/modules/jsdoc/tagdictionary.js +++ b/modules/jsdoc/tagdictionary.js @@ -80,7 +80,8 @@ canHavePdesc : false, // this tag can have a parameter-type desc keepsWhitespace : false, // don't try to tidy up the whitespace in this tag? impliesTag : false, // this tag implies another tag - isScalar : false // can only have a single value (first wins) + isScalar : false, // can only have a single value (first wins) + forceArray : false // must always be an array }; /** Syntax: @access (private|public|protected) @@ -209,7 +210,8 @@ setsDocletKind: true, setsDocletDesc: true, setsDocletName: true, - setsDocletDocspace: true + setsDocletDocspace: true, + impliesTag: 'scope global' }); /** Syntax: @method|function @@ -300,7 +302,8 @@ isExported: true, canHaveType: true, canHavePname: true, - canHavePdesc: true + canHavePdesc: true, + forceArray: true }); /** Syntax: @type @@ -474,7 +477,8 @@ new TagDefinition('tag', { isExported: true, canHavePname: true, - canHavePdesc: true + canHavePdesc: true, + forceArray: true }); /** Syntax: @deprecated diff --git a/test/runall.js b/test/runall.js index e31ecafb..a58737d0 100644 --- a/test/runall.js +++ b/test/runall.js @@ -25,7 +25,7 @@ load(BASEDIR + '/test/tests/22_tag_preserve.js'); load(BASEDIR + '/test/tests/23_tag_fires.js'); load(BASEDIR + '/test/tests/24_tag_exception.js'); load(BASEDIR + '/test/tests/25_tag_scope.js'); - +load(BASEDIR + '/test/tests/26_tag_tag.js'); // see http://visionmedia.github.com/jspec/ JSpec.run({ diff --git a/test/samples/jsdoc_test.js b/test/samples/jsdoc_test.js index 4bee20aa..1cf6f9ff 100644 --- a/test/samples/jsdoc_test.js +++ b/test/samples/jsdoc_test.js @@ -298,7 +298,6 @@ Square.prototype.setHeight = function(height){ * @class Circle class is another subclass of Shape * @extends Shape * @param {int} radius The optional radius of this {@link Circle } - * @mixin Square.prototype.setWidth as this.setDiameter */ function Circle(radius){ if (radius) { @@ -342,6 +341,11 @@ Circle.prototype.setRadius = function(radius){ this.radius = radius; } + /** + * @name Circle#setDiameter => Square#setWidth + */ +Circle.prototype.setDiameter = Square.prototype.setWidth; + /** * An example of a class (static) method that acts as a factory for Circle * objects. Given a radius value, this method creates a new Circle. diff --git a/test/samples/tag_tag.js b/test/samples/tag_tag.js new file mode 100644 index 00000000..d8167152 --- /dev/null +++ b/test/samples/tag_tag.js @@ -0,0 +1,15 @@ + /** @namespace foo + @tag hilited + */ + + /** @namespace bar + @tag api developer + */ + + /** @namespace baz + @tag support - experimental + @tag api internal + */ + + /** @namespace zub + */ \ No newline at end of file diff --git a/test/tests/19_tag_param.js b/test/tests/19_tag_param.js index b6067c3b..b11b75e8 100644 --- a/test/tests/19_tag_param.js +++ b/test/tests/19_tag_param.js @@ -55,21 +55,23 @@ }); describe('A doclet with one param tag', function() { - it('should have a `param` property that is an object', function() { + it('should have a `param` property that is an array of one object', function() { var doclet = doclets[2]; expect(doclet).to(have_property, 'param'); - expect(doclet.param).to(be_an, Object); + expect(doclet.param).to(be_an, Array); + expect(doclet.param.length).to(be, 1); + expect(doclet.param[0]).to(be_an, Object); }); }); describe('A doclet with one param tag having a type and a name', function() { - it('should have a `param` property with a `type` and `name`', function() { + it('should have a `param` array with a single member with a `type` and `name`', function() { var doclet = doclets[2]; expect(doclet).to(have_property, 'param'); - expect(doclet.param.type).to(be_an, Array); // types are always arrays - expect(doclet.param.type).to(eql, ['string']); - expect(doclet.param.name).to(be_an, String); - expect(doclet.param.name).to(eql, 'str'); + expect(doclet.param[0].type).to(be_an, Array); // types are always arrays + expect(doclet.param[0].type).to(eql, ['string']); + expect(doclet.param[0].name).to(be_an, String); + expect(doclet.param[0].name).to(eql, 'str'); }); it('should not have a `desc`', function() { @@ -79,15 +81,15 @@ }); describe('A doclet with one param tag having a type, name and a desc', function() { - it('should have a `param` property with a `type`, `name` and `desc`', function() { + it('should have a `param` array with a single member with a `type`, `name` and `desc`', function() { var doclet = doclets[3]; expect(doclet).to(have_property, 'param'); - expect(doclet.param.type).to(be_an, Array); // types are always arrays - expect(doclet.param.type).to(eql, ['string']); - expect(doclet.param.name).to(be_an, String); - expect(doclet.param.name).to(eql, 'message'); - expect(doclet.param.desc).to(be_an, String); - expect(doclet.param.desc).to(eql, 'the message to encrypt.'); + expect(doclet.param[0].type).to(be_an, Array); // types are always arrays + expect(doclet.param[0].type).to(eql, ['string']); + expect(doclet.param[0].name).to(be_an, String); + expect(doclet.param[0].name).to(eql, 'message'); + expect(doclet.param[0].desc).to(be_an, String); + expect(doclet.param[0].desc).to(eql, 'the message to encrypt.'); }); }); diff --git a/test/tests/26_tag_tag.js b/test/tests/26_tag_tag.js new file mode 100644 index 00000000..9dab10f2 --- /dev/null +++ b/test/tests/26_tag_tag.js @@ -0,0 +1,76 @@ +(function() { + var jsdoc, + doclets; + + JSpec.describe('@scope', function() { + + before(function() { + // docsets can only be created by parsers + jsdoc = { + tag: require('jsdoc/tag'), + parser: require('jsdoc/parser') + }; + jsdoc.parser.parseFiles(BASEDIR + 'test/samples/tag_tag.js'); + + doclets = jsdoc.parser.result.map(function($){ return $.toObject(); }); + }); + + describe('A doclet with single @tag ', function() { + it('should have a `tag` property of type array with a single member', function() { + var doclet = doclets[0]; + expect(doclet).to(have_property, 'tag'); + expect(doclet.tag).to(be_an, Array); + expect(doclet.tag.length).to(be, 1); + }); + + it('that tag should have a name property set to , and no description property', function() { + var doclet = doclets[0]; + expect(doclet.tag[0]).to(have_property, 'name'); + expect(doclet.tag[0].desc).to(be_undefined); + expect(doclet.tag[0].name).to(be_an, String); + expect(doclet.tag[0].name).to(be, 'hilited'); + }); + }); + + describe('A doclet with single @tag ', function() { + it('should have a tag with a name property set to , and description set to ', function() { + var doclet = doclets[1]; + expect(doclet.tag[0]).to(have_property, 'name'); + expect(doclet.tag[0]).to(have_property, 'desc'); + expect(doclet.tag[0].name).to(be_an, String); + expect(doclet.tag[0].name).to(be, 'api'); + + expect(doclet.tag[0].desc).to(be_an, String); + expect(doclet.tag[0].desc).to(be, 'developer'); + }); + }); + + describe('A doclet with two @tag ', function() { + it('should have a tag property set to an array of length 2', function() { + var doclet = doclets[2]; + expect(doclet).to(have_property, 'tag'); + expect(doclet.tag).to(be_an, Array); + expect(doclet.tag.length).to(be, 2); + }); + }); + + describe('A doclet with two @tag - ', function() { + it('should have a tag property with a name property set to , and description set to ', function() { + var doclet = doclets[2]; + expect(doclet.tag[0]).to(have_property, 'name'); + expect(doclet.tag[0]).to(have_property, 'desc'); + expect(doclet.tag[0].name).to(be, 'support'); + expect(doclet.tag[0].desc).to(be, 'experimental'); + + }); + }); + + describe('A doclet with no @tag', function() { + it('should not have a tag property', function() { + var doclet = doclets[3]; + expect(doclet.tag).to(be_undefined); + }); + }); + + }); +})();