Merge branch 'types'

This commit is contained in:
Jeff Williams 2013-03-20 08:25:26 -07:00
commit 4bf63b4185
24 changed files with 1155 additions and 363 deletions

View File

@ -18,6 +18,7 @@
],
"dependencies": {
"async": "0.1.22",
"catharsis": "0.5.0",
"crypto-browserify": "git://github.com/dominictarr/crypto-browserify.git#95c5d505",
"github-flavored-markdown": "git://github.com/hegemonic/github-flavored-markdown.git",
"js2xmlparser": "0.1.0",

View File

@ -66,6 +66,16 @@ The source code for Async.js is available at:
https://github.com/caolan/async
## Catharsis ##
Catharsis is distributed under the MIT license, which is reproduced above.
Copyright (c) 2012-2013 Jeff Williams.
The source code for Catharsis is available at:
https://github.com/hegemonic/catharsis
## crypto-browserify ##
License information for crypto-browserify is not available. It is assumed that

View File

@ -146,8 +146,8 @@ exports.defineTags = function(dictionary) {
// Allow augments value to be specified as a normal type, e.g. {Type}
onTagText: function(text) {
var type = require('jsdoc/tag/type'),
tagType = type.getTagInfo(text, false, true);
return tagType.type || text;
tagType = type.parse(text, false, true);
return tagType.typeExpression || text;
},
onTagged: function(doclet, tag) {
doclet.augment( firstWordOf(tag.value) );

115
lib/jsdoc/tag/inline.js Normal file
View File

@ -0,0 +1,115 @@
/**
* @module jsdoc/tag/inline
*
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
* Information about the result of extracting an inline tag from a text string.
*
* @typedef {Object} InlineTagInfo
* @memberof module:jsdoc/tag/inline
* @property {?string} tag - The tag whose text was found, or `null` if no tag was specified.
* @property {string} text - The tag text that was found.
* @property {string} newString - The updated text string after extracting or replacing the inline
* tag.
*/
/**
* Function that replaces an inline tag with other text.
*
* @callback InlineTagReplacer
* @memberof module:jsdoc/tag/inline
* @param {string} string - The complete string containing the inline tag.
* @param {string} completeTag - The entire inline tag, including its enclosing braces.
* @param {string} tagText - The text contained in the inline tag.
* @return {string} An updated version of the string that contained the inline tag.
*/
/** @private */
function unescapeBraces(text) {
return text.replace(/\\\{/g, '{')
.replace(/\\\}/g, '}');
}
/**
* Replace an inline tag with other text.
*
* To replace untagged text that is enclosed in braces, set the `tag` parameter to `null`.
*
* @param {string} string - The string in which to replace the inline tag.
* @param {?string} tag - The inline tag that must follow the opening brace (for example, `@link`).
* @param {module:jsdoc/tag/inline.InlineTagReplacer} replacer - The function that is used to
* replace text in the string.
* @return {module:jsdoc/tag/inline.InlineTagInfo} The updated string, as well as information about
* the inline tag.
*/
exports.replaceInlineTag = function(string, tag, replacer) {
string = string || '';
tag = tag || '';
var count = 0;
var position = 0;
var completeTag = '';
var text = '';
var start = '{' + tag;
var startIndex = string.indexOf(start);
var textStartIndex;
if (startIndex !== -1) {
// advance to the first character after `start`
position = textStartIndex = startIndex + start.length;
count++;
while (position < string.length) {
switch (string[position]) {
case '\\':
// backslash is an escape character, so skip the next character
position++;
break;
case '{':
count++;
break;
case '}':
count--;
break;
default:
// do nothing
}
if (count === 0) {
completeTag = string.slice(startIndex, position + 1);
text = string.slice(textStartIndex, position).trim();
break;
}
position++;
}
}
string = replacer.call(this, string, completeTag, text);
return {
tag: tag || null,
text: unescapeBraces(text),
newString: string.trim()
};
};
/**
* Extract the first portion of a string that is enclosed in braces, with the `tag` parameter
* immediately following the opening brace.
*
* To extract untagged text that is enclosed in braces, omit the `tag` parameter.
*
* @param {string} string - The string from which to extract text.
* @param {?string} tag - The inline tag that must follow the opening brace (for example, `@link`).
* @return {module:jsdoc/tag/inline.InlineTagInfo} Information about the string and inline tag.
*/
exports.extractInlineTag = function(string, tag) {
return exports.replaceInlineTag(string, tag, function(string, completeTag,
tagText) {
return string.replace(completeTag, '');
});
};

View File

@ -1,59 +1,26 @@
/**
@module jsdoc/tag/type
@author Michael Mathews <micmath@gmail.com>
@author Jeff Williams <jeffrey.l.williams@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
* @module jsdoc/tag/type
*
* @author Michael Mathews <micmath@gmail.com>
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
function parseTypes(type) {
var types = [];
if ( type.indexOf('|') !== -1 ) {
// remove optional parens, like: { ( string | number ) }
// see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
type = RegExp.$1;
}
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
return types;
}
/** @private */
function trim(text) {
return text.trim();
}
function getTagInfo(tagValue, canHaveName, canHaveType) {
var extractInlineTag = require('jsdoc/tag/inline').extractInlineTag;
var getTagInfo = exports.getTagInfo = function(tagValue, canHaveName, canHaveType) {
var name = '',
type = '',
text = tagValue,
count = 0;
var name = '';
var typeExpression = '';
var text = tagValue;
var typeAndText;
var typeOverride;
// type expressions start with '{'
if (canHaveType && tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
}
}
if (canHaveType) {
typeAndText = extractInlineTag(text);
typeExpression = typeAndText.text || typeExpression;
text = typeAndText.newString;
}
if (canHaveName) {
@ -63,32 +30,180 @@ var getTagInfo = exports.getTagInfo = function(tagValue, canHaveName, canHaveTyp
text = RegExp.$3;
}
return { name: name, type: type, text: text };
};
// an inline @type tag, like {@type Foo}, overrides the type expression
if (canHaveType) {
typeOverride = extractInlineTag(text, '@type');
typeExpression = typeOverride.text || typeExpression;
text = typeOverride.newString;
}
return {
name: name,
typeExpression: typeExpression,
text: text
};
}
/**
@param {string} tagValue
@param {boolean} canHaveName
@param {boolean} canHaveType
@returns {object} Hash with name, type, text, optional, nullable, variable, and defaultvalue properties
* Information provided in a JSDoc tag.
*
* @typedef {Object} TagInfo
* @memberof module:jsdoc/tag/type
* @property {string} TagInfo.defaultvalue - The default value of the member.
* @property {string} TagInfo.name - The name of the member (for example, `myParamName`).
* @property {boolean} TagInfo.nullable - Indicates whether the member can be set to `null` or
* `undefined`.
* @property {boolean} TagInfo.optional - Indicates whether the member is optional.
* @property {string} TagInfo.text - Descriptive text for the member (for example, `The user's email
* address.`).
* @property {Array.<string>} TagInfo.type - The type or types that the member can contain (for
* example, `string` or `MyNamespace.MyClass`).
* @property {string} TagInfo.typeExpression - The type expression that was parsed to identify the
* types.
* @property {boolean} TagInfo.variable - Indicates whether the number of members that are provided
* can vary (for example, in a function that accepts any number of parameters).
*/
/**
* Extract JSDoc-style type information from the name specified in the tag info, including the
* member name; whether the member is optional; and the default value of the member.
*
* @private
* @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
* @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
*/
function parseName(tagInfo) {
// like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
if ( /^\[\s*(.+?)\s*\]$/.test(tagInfo.name) ) {
tagInfo.name = RegExp.$1;
tagInfo.optional = true;
// like 'foo=bar' or 'foo = bar'
if ( /^(.+?)\s*=\s*(.+)$/.test(tagInfo.name) ) {
tagInfo.name = RegExp.$1;
tagInfo.defaultvalue = RegExp.$2;
}
}
return tagInfo;
}
/** @private */
function getTypeStrings(parsedType) {
var types = [];
var catharsis = require('catharsis');
var TYPES = catharsis.Types;
var util = require('util');
switch(parsedType.type) {
case TYPES.AllLiteral:
types.push('*');
break;
case TYPES.FunctionType:
types.push('function');
break;
case TYPES.NameExpression:
types.push(parsedType.name);
break;
case TYPES.NullLiteral:
types.push('null');
break;
case TYPES.RecordType:
types.push('Object');
break;
case TYPES.TypeApplication:
types.push( catharsis.stringify(parsedType) );
break;
case TYPES.TypeUnion:
parsedType.elements.forEach(function(element) {
types = types.concat( getTypeStrings(element) );
});
break;
case TYPES.UndefinedLiteral:
types.push('undefined');
break;
case TYPES.UnknownLiteral:
types.push('?');
break;
default:
// this shouldn't happen
throw new Error( util.format('unrecognized type %s in parsed type: %j', parsedType.type,
parsedType) );
}
return types;
}
/**
* Extract JSDoc-style and Closure Compiler-style type information from the type expression
* specified in the tag info.
*
* @private
* @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
* @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
*/
function parseTypeExpression(tagInfo) {
var catharsis = require('catharsis');
var util = require('util');
var errorMessage;
var parsedType;
// don't try to parse empty type expressions
if (!tagInfo.typeExpression) {
return tagInfo;
}
try {
parsedType = catharsis.parse(tagInfo.typeExpression, {jsdoc: true});
}
catch (e) {
errorMessage = util.format('unable to parse the type expression "%s": %s',
tagInfo.typeExpression, e.message);
require('jsdoc/util/error').handle( new Error(errorMessage) );
}
if (parsedType) {
tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType) );
['optional', 'nullable', 'variable'].forEach(function(key) {
if (parsedType[key] !== null && parsedType[key] !== undefined) {
tagInfo[key] = parsedType[key];
}
});
}
return tagInfo;
}
// TODO: allow users to add/remove type parsers (perhaps via plugins)
var typeParsers = [parseName, parseTypeExpression];
/**
* Parse the value of a JSDoc tag.
*
* @param {string} tagValue - The value of the tag. For example, the tag `@param {string} name` has
* a value of `{string} name`.
* @param {boolean} canHaveName - Indicates whether the value can include a member name.
* @param {boolean} canHaveType - Indicates whether the value can include a type expression that
* describes the member.
* @return {module:jsdoc/tag/type.TagInfo} Information obtained from the tag.
*/
exports.parse = function(tagValue, canHaveName, canHaveType) {
if (typeof tagValue !== 'string') { tagValue = ''; }
var tagInfo = getTagInfo(tagValue, canHaveName, canHaveType);
tagInfo.type = tagInfo.type || [];
// extract JSDoc-style type info, then Closure Compiler-style type info
tagInfo = require('jsdoc/tag/type/jsdocType').parse(tagInfo);
tagInfo = require('jsdoc/tag/type/closureCompilerType').parse(tagInfo);
typeParsers.forEach(function(parser) {
tagInfo = parser.call(this, tagInfo);
});
return {
name: tagInfo.name,
type: parseTypes(tagInfo.type), // make it into an array
text: tagInfo.text,
optional: tagInfo.optional,
nullable: tagInfo.nullable,
variable: tagInfo.variable,
defaultvalue: tagInfo.defaultvalue
};
// if we wanted a type, but the parsers didn't add any type names, use the type expression
if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) {
tagInfo.type = [tagInfo.typeExpression];
}
return tagInfo;
};

View File

@ -1,64 +0,0 @@
/**
@module jsdoc/tag/type/closureCompilerType
@author Michael Mathews <micmath@gmail.com>
@author Jeff Williams <jeffrey.l.williams@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
type = RegExp.$1;
optional = true;
}
return { type: type, optional: optional };
}
function parseNullable(type) {
var nullable = null;
// {?sometype} means nullable, {!sometype} means not-nullable
if ( /^([\?\!])(.+)$/.test(type) ) {
type = RegExp.$2;
nullable = (RegExp.$1 === '?')? true : false;
}
return { type: type, nullable: nullable };
}
function parseVariable(type) {
var variable = null;
// {...sometype} means variable number of that type
if ( /^(\.\.\.)(.+)$/.test(type) ) {
type = RegExp.$2;
variable = true;
}
return { type: type, variable: variable };
}
/**
Extract Closure Compiler-style type information from the tag info.
@param {object} tagInfo Hash with name, type, and text properties.
@return {object} Hash with name, type, text, optional, nullable, variable, and default properties.
*/
exports.parse = function(tagInfo) {
var optional = parseOptional(tagInfo.type),
nullable = parseNullable(optional.type),
variable = parseVariable(nullable.type);
return {
name: tagInfo.name,
type: variable.type,
text: tagInfo.text,
optional: tagInfo.optional || optional.optional, // don't override if already true
nullable: nullable.nullable,
variable: variable.variable,
defaultvalue: tagInfo.defaultvalue
};
};

View File

@ -1,40 +0,0 @@
/**
@module jsdoc/tag/type/jsdocType
@author Michael Mathews <micmath@gmail.com>
@author Jeff Williams <jeffrey.l.williams@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
Extract JSDoc-style type information from the tag info.
@param {object} tagInfo Hash with name, type, text, optional, nullable, variable, and default properties.
@return {object} Hash with the same properties as tagInfo.
*/
exports.parse = function(tagInfo) {
var name = tagInfo.name,
optional,
tagDefault;
// like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
if ( /^\[\s*(.+?)\s*\]$/.test(name) ) {
name = RegExp.$1;
optional = true;
// like 'foo=bar' or 'foo = bar'
if ( /^(.+?)\s*=\s*(.+)$/.test(name) ) {
name = RegExp.$1;
tagDefault = RegExp.$2;
}
}
return {
name: name,
type: tagInfo.type,
text: tagInfo.text,
optional: optional,
nullable: tagInfo.nullable,
variable: tagInfo.variable,
defaultvalue: tagDefault
};
};

View File

@ -25,7 +25,7 @@ var tutorial = require('jsdoc/tutorial'),
*/
function isTutorialJSON(json) {
// if conf.title exists or conf.children exists, it is metadata for a tutorial
return (json.hasOwnProperty('title') || json.hasOwnProperty('children'));
return (hasOwnProp.call(json, 'title') || hasOwnProp.call(json, 'children'));
}
/** Helper function that adds tutorial configuration to the `conf` variable.
@ -50,7 +50,7 @@ function addTutorialConf(name, meta) {
if (isTutorialJSON(meta)) {
// if the children are themselves tutorial defintions as opposed to an
// array of strings, add each child.
if (meta.hasOwnProperty('children') && !Array.isArray(meta.children)) {
if (hasOwnProp.call(meta, 'children') && !Array.isArray(meta.children)) {
names = Object.keys(meta.children);
for (i = 0; i < names.length; ++i) {
addTutorialConf(names[i], meta.children[names[i]]);
@ -59,7 +59,7 @@ function addTutorialConf(name, meta) {
meta.children = names;
}
// check if the tutorial has already been defined...
if (conf.hasOwnProperty(name)) {
if (hasOwnProp.call(conf, name)) {
error.handle(new Error("Tutorial " + name + "'s metadata is defined multiple times, only the first will be used."));
} else {
conf[name] = meta;
@ -77,7 +77,7 @@ function addTutorialConf(name, meta) {
@param {tutorial.Tutorial} current - New tutorial.
*/
exports.addTutorial = function(current) {
if (tutorials.hasOwnProperty(current.name)) {
if (hasOwnProp.call(tutorials, current.name)) {
error.handle(new Error("Tutorial with name " + current.name + " exists more than once, not adding (same name, different file extensions?)"));
} else {
tutorials[current.name] = current;

View File

@ -100,10 +100,41 @@ var tutorialLinkMap = {
var longnameToUrl = exports.longnameToUrl = linkMap.longnameToUrl;
/**
* Retrieve an HTML link to the member with the specified longname. If the longname is not
* associated with a URL, this method returns the link text, if provided, or the longname.
*
* This method supports type applications that can contain one or more types, such as
* `Array.<MyClass>` or `Array.<(MyClass|YourClass)>`. In these examples, the method attempts to
* replace `Array`, `MyClass`, and `YourClass` with links to the appropriate types. The link text
* is ignored for type applications.
*
* @param {string} longname - The longname that is the target of the link.
* @param {string=} linktext - The text to display for the link, or `longname` if no text is
* provided.
* @param {string=} cssClass - The CSS class (or classes) to include in the link's `<a>` tag.
* @return {string} The HTML link, or a plain-text string if the link is not available.
*/
var linkto = exports.linkto = function(longname, linktext, cssClass) {
var catharsis = require('catharsis');
var classString = cssClass ? util.format(' class="%s"', cssClass) : '';
var text = linktext || longname;
var url = hasOwnProp.call(longnameToUrl, longname) && longnameToUrl[longname];
var parsedType;
// type applications require special treatment
var typeAppInfo = /(\S+)<(\S+)>/.exec(longname);
if (typeAppInfo) {
typeAppInfo[1] = linkto(typeAppInfo[1], null, cssClass);
parsedType = catharsis.parse(typeAppInfo[2], {jsdoc: true});
typeAppInfo[2] = catharsis.stringify(parsedType, {
cssClass: cssClass,
htmlSafe: true,
links: longnameToUrl
});
return typeAppInfo[1] + '&lt;' + typeAppInfo[2] + '>';
}
if (!url) {
return text;

16
node_modules/catharsis/LICENSE generated vendored Normal file
View File

@ -0,0 +1,16 @@
Copyright (c) 2012-2013 Jeff Williams
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

119
node_modules/catharsis/catharsis.js generated vendored Normal file
View File

@ -0,0 +1,119 @@
/**
* catharsis 0.4.2
* A parser for Google Closure Compiler type expressions, powered by PEG.js.
*
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
* @license MIT License <http://opensource.org/licenses/mit-license.php/>
*/
'use strict';
var parse = require('./lib/parser').parse;
var stringify = require('./lib/stringify');
var typeExpressionCache = {
normal: {},
jsdoc: {}
};
var parsedTypeCache = {
normal: {},
htmlSafe: {}
};
function getTypeExpressionCache(options) {
if (options.useCache === false) {
return null;
} else if (options.jsdoc === true) {
return typeExpressionCache.jsdoc;
} else {
return typeExpressionCache.normal;
}
}
function getParsedTypeCache(options) {
if (options.useCache === false || options.links !== null || options.links !== undefined) {
return null;
} else if (options.htmlSafe === true) {
return parsedTypeCache.htmlSafe;
} else {
return parsedTypeCache.normal;
}
}
// can't return the original if any of the following are true:
// 1. restringification was requested
// 2. htmlSafe option was requested
// 3. links option was provided
// 4. typeExpression property is missing
function canReturnOriginalExpression(parsedType, options) {
return options.restringify !== true && options.htmlSafe !== true &&
(options.links === null || options.links === undefined) &&
Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression');
}
function cachedParse(expr, options) {
var cache = getTypeExpressionCache(options);
var parsedType;
if (cache && cache[expr]) {
return cache[expr];
} else {
parsedType = parse(expr, options);
Object.defineProperties(parsedType, {
typeExpression: {
value: expr
},
jsdoc: {
value: options.jsdoc === true ? true : false
}
});
parsedType = Object.freeze(parsedType);
if (cache) {
cache[expr] = parsedType;
}
return parsedType;
}
}
function cachedStringify(parsedType, options) {
var cache = getParsedTypeCache(options);
var json;
if (canReturnOriginalExpression(parsedType, options)) {
return parsedType.typeExpression;
} else if (cache) {
json = JSON.stringify(parsedType);
cache[json] = cache[json] || stringify(parsedType, options);
return cache[json];
} else {
return stringify(parsedType, options);
}
}
function Catharsis() {
this.Types = require('./lib/types');
}
Catharsis.prototype.parse = function(typeExpr, options) {
options = options || {};
return cachedParse(typeExpr, options);
};
Catharsis.prototype.stringify = function(parsedType, options) {
options = options || {};
var result;
result = cachedStringify(parsedType, options);
if (options.validate) {
this.parse(result, options);
}
return result;
};
module.exports = new Catharsis();

3
node_modules/catharsis/lib/parser.js generated vendored Normal file

File diff suppressed because one or more lines are too long

257
node_modules/catharsis/lib/stringify.js generated vendored Normal file
View File

@ -0,0 +1,257 @@
'use strict';
var Types = require('./types');
function Stringifier(options) {
this._options = options || {};
// in a list of function signature params, repeatable params are stringified differently
this._inFunctionSignatureParams = false;
}
Stringifier.prototype.applications = function(applications) {
if (!applications) {
return '';
}
var parsedApplications = [];
var result = '';
for (var i = 0, l = applications.length; i < l; i++) {
parsedApplications.push(this.type(applications[i]));
}
if (this._options.htmlSafe) {
result = '.&lt;';
} else {
result = '.<';
}
result += parsedApplications.join(', ') + '>';
return result;
};
Stringifier.prototype.elements = function(elements) {
if (!elements) {
return '';
}
var result = [];
for (var i = 0, l = elements.length; i < l; i++) {
result.push(this.type(elements[i]));
}
return '(' + result.join('|') + ')';
};
Stringifier.prototype.name = function(name) {
return name || '';
};
Stringifier.prototype['new'] = function(funcNew) {
return funcNew ? 'new:' + this.type(funcNew) : '';
};
Stringifier.prototype.nullable = function(nullable) {
switch (nullable) {
case true:
return '?';
case false:
return '!';
default:
return '';
}
};
Stringifier.prototype.optional = function(optional) {
/*jshint boss: true */ // TODO: remove after JSHint releases the fix for jshint/jshint#878
if (optional === true) {
return '=';
} else {
return '';
}
};
Stringifier.prototype.params = function(params) {
if (!params || params.length === 0) {
return '';
}
var result = [];
var param;
for (var i = 0, l = params.length; i < l; i++) {
result.push(this.type(params[i]));
}
return result.join(', ');
};
Stringifier.prototype.properties = function(props) {
if (!props) {
return '';
}
var result = [];
for (var i = 0, l = props.length; i < l; i++) {
result.push(this._formatNameAndType(props[i].name, props[i].type));
}
return result;
};
Stringifier.prototype.result = function(result) {
return result ? ': ' + this.type(result) : '';
};
Stringifier.prototype['this'] = function(funcThis) {
return funcThis ? 'this:' + this.type(funcThis) : '';
};
Stringifier.prototype.type = function(type) {
if (!type) {
return '';
}
// nullable comes first
var result = this.nullable(type.nullable);
// next portion varies by type
switch(type.type) {
case Types.AllLiteral:
result += this._formatNameAndType(type, '*');
break;
case Types.FunctionType:
result += this._signature(type);
break;
case Types.NullLiteral:
result += this._formatNameAndType(type, 'null');
break;
case Types.RecordType:
result += this._record(type);
break;
case Types.TypeApplication:
result += this.type(type.expression);
result += this.applications(type.applications);
break;
case Types.UndefinedLiteral:
result += this._formatNameAndType(type, 'undefined');
break;
case Types.TypeUnion:
result += this.elements(type.elements);
break;
case Types.UnknownLiteral:
result += this._formatNameAndType(type, '?');
break;
default:
result += this._formatNameAndType(type);
}
// finally, optionality
result += this.optional(type.optional);
return result;
};
Stringifier.prototype.stringify = Stringifier.prototype.type;
Stringifier.prototype.key = Stringifier.prototype.type;
Stringifier.prototype._record = function(type) {
var fields = this._recordFields(type.fields);
return '{' + fields.join(', ') + '}';
};
Stringifier.prototype._recordFields = function(fields) {
if (!fields) {
return '';
}
var result = [];
var field;
var keyAndValue;
for (var i = 0, l = fields.length; i < l; i++) {
field = fields[i];
keyAndValue = this.key(field.key);
keyAndValue += field.value ? ': ' + this.type(field.value) : '';
result.push(keyAndValue);
}
return result;
};
function combineNameAndType(nameString, typeString) {
var separator = (nameString && typeString) ? ':' : '';
return nameString + separator + typeString;
}
Stringifier.prototype._formatRepeatable = function(nameString, typeString) {
var open = this._inFunctionSignatureParams ? '...[' : '...';
var close = this._inFunctionSignatureParams ? ']' : '';
return open + combineNameAndType(nameString, typeString) + close;
};
Stringifier.prototype._formatNameAndType = function(type, literal) {
var nameString = type.name || literal || '';
var typeString = type.type ? this.type(type.type) : '';
var cssClass;
var openTag;
// replace the type with an HTML link if necessary
if (this._options.links && Object.prototype.hasOwnProperty.call(this._options.links,
nameString)) {
cssClass = this._options.cssClass ? ' class="' + this._options.cssClass + '"' : '';
openTag = '<a href="' + this._options.links[nameString] + '"' + cssClass + '>';
nameString = openTag + nameString + '</a>';
}
if (type.repeatable === true) {
return this._formatRepeatable(nameString, typeString);
} else {
return combineNameAndType(nameString, typeString);
}
};
Stringifier.prototype._signature = function(type) {
var params = [];
var param;
var result;
// these go within the signature's parens, in this order
var props = [
'new',
'this',
'params'
];
var prop;
this._inFunctionSignatureParams = true;
for (var i = 0, l = props.length; i < l; i++) {
prop = props[i];
param = this[prop](type[prop]);
if (param.length > 0) {
params.push(param);
}
}
this._inFunctionSignatureParams = false;
result = 'function(' + params.join(', ') + ')';
result += this.result(type.result);
return result;
};
module.exports = function(type, options) {
return new Stringifier(options).stringify(type);
};

24
node_modules/catharsis/lib/types.js generated vendored Normal file
View File

@ -0,0 +1,24 @@
'use strict';
module.exports = Object.freeze({
// `*`
AllLiteral: 'AllLiteral',
// like `blah` in `{blah: string}`
FieldType: 'FieldType',
// like `function(string): string`
FunctionType: 'FunctionType',
// any string literal, such as `string` or `My.Namespace`
NameExpression: 'NameExpression',
// null
NullLiteral: 'NullLiteral',
// like `{foo: string}`
RecordType: 'RecordType',
// like `Array.<string>`
TypeApplication: 'TypeApplication',
// like `(number|string)`
TypeUnion: 'TypeUnion',
// undefined
UndefinedLiteral: 'UndefinedLiteral',
// `?`
UnknownLiteral: 'UnknownLiteral'
});

44
node_modules/catharsis/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{
"name": "jsdoc",
"version": "3.2.0-dev",
"revision": "1359085455394",
"revision": "1363617959876",
"description": "An API documentation generator for JavaScript.",
"keywords": [ "documentation", "javascript" ],
"licenses": [
@ -18,6 +18,7 @@
],
"dependencies": {
"async": "0.1.22",
"catharsis": "0.5.0",
"crypto-browserify": "git://github.com/dominictarr/crypto-browserify.git#95c5d505",
"github-flavored-markdown": "git://github.com/hegemonic/github-flavored-markdown.git",
"js2xmlparser": "0.1.0",

View File

@ -0,0 +1,201 @@
/*global describe: true, expect: true, it: true */
describe('jsdoc/tag/inline', function() {
var jsdoc = {
tag: {
inline: require('jsdoc/tag/inline')
}
};
it('should exist', function() {
expect(jsdoc.tag.inline).toBeDefined();
expect(typeof jsdoc.tag.inline).toBe('object');
});
it('should export a replaceInlineTag function', function() {
expect(jsdoc.tag.inline.replaceInlineTag).toBeDefined();
expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function');
});
it('should export an extractInlineTag function', function() {
expect(jsdoc.tag.inline.extractInlineTag).toBeDefined();
expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function');
});
describe('replaceInlineTag', function() {
it('should throw if the replacer parameter is invalid', function() {
function badReplacerUndefined() {
jsdoc.tag.inline.replaceInlineTag('foo', '@bar');
}
function badReplacerString() {
jsdoc.tag.inline.replaceInlineTag('foo', '@bar', 'hello');
}
expect(badReplacerUndefined).toThrow();
expect(badReplacerString).toThrow();
});
it('should not find anything if there is no text in braces', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('braceless text');
expect(completeTag).toBe('');
expect(tagText).toBe('');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('braceless text', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('');
expect(result.newString).toBe('braceless text');
});
it('should cope with bad escapement at the end of the string', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('bad {escapement \\');
expect(completeTag).toBe('');
expect(tagText).toBe('');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('bad {escapement \\', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('');
expect(result.newString).toBe('bad {escapement \\');
});
it('should handle escaped braces correctly', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('a {braces \\} test}');
expect(completeTag).toBe('{braces \\} test}');
expect(tagText).toBe('braces \\} test');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('a {braces \\} test}', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('braces } test');
expect(result.newString).toBe('a {braces \\} test}');
});
it('should work if the tag is the entire string', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('{text in braces}');
expect(completeTag).toBe('{text in braces}');
expect(tagText).toBe('text in braces');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('{text in braces}', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('text in braces');
expect(result.newString).toBe('{text in braces}');
});
it('should work if the tag is at the beginning of the string', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('{test string} ahoy');
expect(completeTag).toBe('{test string}');
expect(tagText).toBe('test string');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('{test string} ahoy', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('test string');
expect(result.newString).toBe('{test string} ahoy');
});
it('should work if the tag is in the middle of the string', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('a {test string} yay');
expect(completeTag).toBe('{test string}');
expect(tagText).toBe('test string');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('a {test string} yay', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('test string');
expect(result.newString).toBe('a {test string} yay');
});
it('should work if the tag is at the end of the string', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('a {test string}');
expect(completeTag).toBe('{test string}');
expect(tagText).toBe('test string');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('a {test string}', null, replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('test string');
expect(result.newString).toBe('a {test string}');
});
it('should replace the string with the specified value', function() {
function replacer() {
return 'REPLACED!';
}
var result = jsdoc.tag.inline.replaceInlineTag('a {test string}', null, replacer);
expect(result.newString).toBe('REPLACED!');
});
it('should work when there are nested braces', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('some {{double}} braces');
expect(completeTag).toBe('{{double}}');
expect(tagText).toBe('{double}');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('some {{double}} braces', null,
replacer);
expect(result.tag).toBe(null);
expect(result.text).toBe('{double}');
expect(result.newString).toBe('some {{double}} braces');
});
it('should work when a tag is specified', function() {
function replacer(string, completeTag, tagText) {
expect(string).toBe('a {@foo tag} test');
expect(completeTag).toBe('{@foo tag}');
expect(tagText).toBe('tag');
return string;
}
var result = jsdoc.tag.inline.replaceInlineTag('a {@foo tag} test', '@foo', replacer);
expect(result.tag).toBe('@foo');
expect(result.text).toBe('tag');
expect(result.newString).toBe('a {@foo tag} test');
});
});
// largely covered by the replaceInlineTag tests
describe('extractInlineTag', function() {
it('should work when there is no tag specified', function() {
var result = jsdoc.tag.inline.extractInlineTag('some {braced text}');
expect(result.tag).toBe(null);
expect(result.text).toBe('braced text');
expect(result.newString).toBe('some');
});
it('should work when a tag is specified', function() {
var result = jsdoc.tag.inline.extractInlineTag('some {@tagged text}', '@tagged');
expect(result.tag).toBe('@tagged');
expect(result.text).toBe('text');
expect(result.newString).toBe('some');
});
});
});

View File

@ -3,16 +3,16 @@
function buildText(type, name, desc) {
var text = '';
if (type) {
text += "{" + type + "}";
text += '{' + type + '}';
if (name || desc) {
text += " ";
text += ' ';
}
}
if (name) {
text += name;
if (desc) {
text += " ";
text += ' ';
}
}
@ -23,129 +23,157 @@ function buildText(type, name, desc) {
return text;
}
describe("jsdoc/tag/type", function() {
describe('jsdoc/tag/type', function() {
var jsdoc = {
tag: {
type: require('jsdoc/tag/type')
}
};
it("should exist", function() {
it('should exist', function() {
expect(jsdoc.tag.type).toBeDefined();
expect(typeof jsdoc.tag.type).toEqual("object");
expect(typeof jsdoc.tag.type).toBe('object');
});
it("should export a getTagInfo function", function() {
expect(jsdoc.tag.type.getTagInfo).toBeDefined();
expect(typeof jsdoc.tag.type.getTagInfo).toEqual("function");
});
it("should export a parse function", function() {
it('should export a parse function', function() {
expect(jsdoc.tag.type.parse).toBeDefined();
expect(typeof jsdoc.tag.type.parse).toEqual("function");
expect(typeof jsdoc.tag.type.parse).toBe('function');
});
describe("getTagInfo", function() {
it("should return an object with name, type, and text properties", function() {
var info = jsdoc.tag.type.getTagInfo("");
describe('parse', function() {
it('should return an object with name, type, and text properties', function() {
var info = jsdoc.tag.type.parse('');
expect(info.name).toBeDefined();
expect(info.type).toBeDefined();
expect(info.text).toBeDefined();
});
it("should not extract a name or type if canHaveName and canHaveType are not set", function() {
var desc = "{number} foo The foo parameter.";
var info = jsdoc.tag.type.getTagInfo(desc);
expect(info.type).toEqual('');
expect(info.name).toEqual('');
expect(info.text).toEqual(desc);
it('should not extract a name or type if canHaveName and canHaveType are not set', function() {
var desc = '{number} foo The foo parameter.';
var info = jsdoc.tag.type.parse(desc);
expect(info.type).toEqual([]);
expect(info.name).toBe('');
expect(info.text).toBe(desc);
});
it("should extract a name, but not a type, if canHaveName === true and canHaveType === false", function() {
var name = "bar";
var desc = "The bar parameter.";
var info = jsdoc.tag.type.getTagInfo( buildText(null, name, desc), true, false );
expect(info.type).toEqual('');
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
it('should extract a name, but not a type, if canHaveName === true and canHaveType === false', function() {
var name = 'bar';
var desc = 'The bar parameter.';
var info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false );
expect(info.type).toEqual([]);
expect(info.name).toBe(name);
expect(info.text).toBe(desc);
});
it("should extract a type, but not a name, if canHaveName === false and canHaveType === true", function() {
var type = "boolean";
var desc = "Set to true on alternate Thursdays.";
var info = jsdoc.tag.type.getTagInfo( buildText(type, null, desc), false, true );
expect(info.type).toEqual(type);
expect(info.name).toEqual('');
expect(info.text).toEqual(desc);
it('should extract a type, but not a name, if canHaveName === false and canHaveType === true', function() {
var type = 'boolean';
var desc = 'Set to true on alternate Thursdays.';
var info = jsdoc.tag.type.parse( buildText(type, null, desc), false, true );
expect(info.type).toEqual([type]);
expect(info.name).toBe('');
expect(info.text).toBe(desc);
});
it("should extract a name and type if canHaveName and canHaveType are true", function() {
var type = "string";
var name = "baz";
var desc = "The baz parameter.";
var info = jsdoc.tag.type.getTagInfo( buildText(type, name, desc), true, true );
expect(info.type).toEqual(type);
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
it('should extract a name and type if canHaveName and canHaveType are true', function() {
var type = 'string';
var name = 'baz';
var desc = 'The baz parameter.';
var info = jsdoc.tag.type.parse( buildText(type, name, desc), true, true );
expect(info.type).toEqual([type]);
expect(info.name).toBe(name);
expect(info.text).toBe(desc);
});
it("should work with JSDoc-style optional parameters", function() {
var name = "[qux]";
var desc = "The qux parameter.";
var info = jsdoc.tag.type.getTagInfo( buildText(null, name, desc), true, false );
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
name = "[ qux ]";
info = jsdoc.tag.type.getTagInfo( buildText(null, name, desc), true, false );
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
name = "[qux=hooray]";
info = jsdoc.tag.type.getTagInfo( buildText(null, name, desc), true, false );
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
name = "[ qux = hooray ]";
info = jsdoc.tag.type.getTagInfo( buildText(null, name, desc), true, false );
expect(info.name).toEqual(name);
expect(info.text).toEqual(desc);
});
});
describe("parse", function() {
it("should report optional types correctly no matter which syntax we use", function() {
var desc = "{string} [foo]";
it('should report optional types correctly no matter which syntax we use', function() {
var desc = '{string} [foo]';
var info = jsdoc.tag.type.parse(desc, true, true);
expect(info.optional).toEqual(true);
expect(info.optional).toBe(true);
desc = "{string=} [foo]";
desc = '{string=} [foo]';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.optional).toEqual(true);
expect(info.optional).toBe(true);
});
it("should return the types as an array", function() {
var desc = "{string} foo";
it('should return the types as an array', function() {
var desc = '{string} foo';
var info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ["string"] );
expect(info.type).toEqual( ['string'] );
});
it("should recognize the entire list of possible types", function() {
var desc = "{string|number} foo";
it('should recognize the entire list of possible types', function() {
var desc = '{(string|number)} foo';
var info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ["string", "number"] );
expect(info.type).toEqual( ['string', 'number'] );
desc = "{ string | number } foo";
desc = '{ ( string | number ) } foo';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ["string", "number"] );
expect(info.type).toEqual( ['string', 'number'] );
desc = "{ ( string | number)} foo";
desc = '{ ( string | number)} foo';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ["string", "number"] );
expect(info.type).toEqual( ['string', 'number'] );
desc = "{(string|number|boolean|function)} foo";
desc = '{(string|number|boolean|function)} foo';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ["string", "number", "boolean", "function"] );
expect(info.type).toEqual( ['string', 'number', 'boolean', 'function'] );
});
it('should override the type expression if an inline @type tag is specified', function() {
var desc = '{Object} cookie {@type Monster}';
var info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ['Monster'] );
expect(info.text).toBe('');
desc = '{Object} cookie - {@type Monster}';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ['Monster'] );
expect(info.text).toBe('');
desc = '{Object} cookie - The cookie parameter. {@type Monster}';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ['Monster'] );
expect(info.text).toBe('The cookie parameter.');
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)}';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ['Monster', 'Jar'] );
expect(info.text).toBe('The cookie parameter.');
desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)} Mmm, cookie.';
info = jsdoc.tag.type.parse(desc, true, true);
expect(info.type).toEqual( ['Monster', 'Jar'] );
expect(info.text).toBe('The cookie parameter. Mmm, cookie.');
});
describe('JSDoc-style type info', function() {
it('should parse JSDoc-style optional parameters', function() {
var name = '[qux]';
var desc = 'The qux parameter.';
var info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false );
expect(info.name).toBe('qux');
expect(info.text).toBe(desc);
expect(info.optional).toBe(true);
name = '[ qux ]';
info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false );
expect(info.name).toBe('qux');
expect(info.text).toBe(desc);
expect(info.optional).toBe(true);
name = '[qux=hooray]';
info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false );
expect(info.name).toBe('qux');
expect(info.text).toBe(desc);
expect(info.optional).toBe(true);
expect(info.defaultvalue).toBe('hooray');
name = '[ qux = hooray ]';
info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false );
expect(info.name).toBe('qux');
expect(info.text).toBe(desc);
expect(info.optional).toBe(true);
expect(info.defaultvalue).toBe('hooray');
});
});
});
});

View File

@ -1,25 +0,0 @@
/*global describe: true, expect: true, it: true */
describe('jsdoc/tag/type/closureCompilerType', function() {
// TODO: more tests
var type = require('jsdoc/tag/type/closureCompilerType');
it('should exist', function() {
expect(type).toBeDefined();
expect(typeof type).toEqual('object');
});
it('should export a parse function', function() {
expect(type.parse).toBeDefined();
expect(typeof type.parse).toEqual('function');
});
describe('parse', function() {
it('should correctly parse types that are both optional and nullable', function() {
var info = type.parse( {type: '?string='} );
expect(info.type).toEqual('string');
expect(info.optional).toEqual(true);
expect(info.nullable).toEqual(true);
});
});
});

View File

@ -1,68 +0,0 @@
/*global describe: true, expect: true, it: true */
var hasOwnProp = Object.prototype.hasOwnProperty;
describe("jsdoc/tag/type/jsdocType", function() {
var jsdocType = require("jsdoc/tag/type/jsdocType");
it("should exist", function() {
expect(jsdocType).toBeDefined();
expect(typeof jsdocType).toEqual("object");
});
it("should export a parse function", function() {
expect(jsdocType.parse).toBeDefined();
expect(typeof jsdocType.parse).toEqual("function");
});
describe("parse", function() {
it("should recognize optional properties without default values", function() {
var info = jsdocType.parse( { name: "[foo]" } );
expect(info.name).toEqual("foo");
expect(info.optional).toEqual(true);
expect( info.defaultvalue ).toEqual(null);
info = jsdocType.parse( { name: "[ bar ]" } );
expect(info.name).toEqual("bar");
expect(info.optional).toEqual(true);
expect( info.defaultvalue ).toEqual(null);
});
it("should recognize optional properties with default values", function() {
var info = jsdocType.parse( { name: "[foo=bar]" } );
expect(info.name).toEqual("foo");
expect(info.optional).toEqual(true);
expect( info.defaultvalue ).toEqual("bar");
info = jsdocType.parse( { name: "[ baz = qux ]" } );
expect(info.name).toEqual("baz");
expect(info.optional).toEqual(true);
expect( info.defaultvalue ).toEqual("qux");
});
it("should only change the `name`, `optional`, and `defaultvalue` properties", function() {
var obj = {
name: "[foo=bar]",
type: "boolean|string",
text: "Sample text.",
optional: null,
nullable: null,
variable: null,
defaultvalue: null
};
var shouldChange = [ "name", "optional", "defaultvalue" ];
var info = jsdocType.parse(obj);
for (var key in info) {
if ( hasOwnProp.call(info, key) ) {
if ( shouldChange.indexOf(key) !== -1 ) {
expect( info[key] ).not.toEqual( obj[key] );
}
else {
expect( info[key] ).toEqual( obj[key] );
}
}
}
});
});
});

View File

@ -223,10 +223,12 @@ describe("jsdoc/util/templateHelper", function() {
describe("linkto", function() {
beforeEach(function() {
helper.longnameToUrl.linktoTest = 'test.html';
helper.longnameToUrl.LinktoFakeClass = 'fakeclass.html';
});
afterEach(function() {
delete helper.longnameToUrl.linktoTest;
delete helper.longnameToUrl.LinktoFakeClass;
});
it('returns the longname if only the longname is specified and has no URL', function() {
@ -260,12 +262,34 @@ describe("jsdoc/util/templateHelper", function() {
expect(link).toBe('<a href="test.html" class="myclass">link text</a>');
});
it("is careful with longnames that are reserved words in JS", function() {
it('is careful with longnames that are reserved words in JS', function() {
// we don't have a registered link for 'constructor' so it should return the text 'link text'.
var link = helper.linkto('constructor', 'link text');
expect(typeof link).toBe('string');
expect(link).toBe('link text');
});
it('works correctly with type applications if only the longname is specified', function() {
var link = helper.linkto('Array.<LinktoFakeClass>');
expect(link).toBe('Array.&lt;<a href="fakeclass.html">LinktoFakeClass</a>>');
});
it('works correctly with type applications if a class is not specified', function() {
var link = helper.linkto('Array.<LinktoFakeClass>', 'link text');
expect(link).toBe('Array.&lt;<a href="fakeclass.html">LinktoFakeClass</a>>');
});
it('works correctly with type applications if a class is specified', function() {
var link = helper.linkto('Array.<LinktoFakeClass>', 'link text', 'myclass');
expect(link).toBe('Array.&lt;<a href="fakeclass.html" class="myclass">LinktoFakeClass' +
'</a>>');
});
it('works correctly with type applications that include a type union', function() {
var link = helper.linkto('Array.<(linktoTest|LinktoFakeClass)>', 'link text');
expect(link).toBe('Array.&lt;(<a href="test.html">linktoTest</a>|' +
'<a href="fakeclass.html">LinktoFakeClass</a>)>');
});
});
describe("htmlsafe", function() {

View File

@ -11,7 +11,7 @@ describe("@param tag", function() {
it('When a symbol has an @param tag with a type before the name, the doclet has a params property that includes that param.', function() {
expect(typeof find.params).toBe('object');
expect(find.params.length).toBe(1);
expect(find.params[0].type.names.join(', ')).toBe('String, Array<String>');
expect(find.params[0].type.names.join(', ')).toBe('String, Array.<String>');
expect(find.params[0].name).toBe('targetName');
expect(find.params[0].description).toBe('The name (or names) of what to find.');
});

View File

@ -6,7 +6,7 @@ describe("@returns tag", function() {
it('When a symbol has an @returns tag with a type and description, the doclet has a returns array that includes that return.', function() {
expect(typeof find.returns).toBe('object');
expect(find.returns.length).toBe(1);
expect(find.returns[0].type.names.join(', ')).toBe('String, Array<String>');
expect(find.returns[0].type.names.join(', ')).toBe('String, Array.<String>');
expect(find.returns[0].description).toBe('The names of the found item(s).');
});

View File

@ -6,7 +6,7 @@ describe("@type tag", function() {
it('When a symbol has an @type tag, the doclet has a type property set to that value\'s type.', function() {
expect(typeof foo.type).toBe('object');
expect(typeof foo.type.names).toBe('object');
expect(foo.type.names.join(', ')).toBe('string, Array<string>');
expect(foo.type.names.join(', ')).toBe('string, Array.<string>');
});
it('When a symbol has an @type tag set to a plain string, the doclet has a type property set to that string as if it were a type.', function() {