This change modifies the build scripts to use --legacy-peer-deps flag when installing npm dependencies to resolve potential peer dependency conflicts during the build process. The change is applied to both bash (build.sh) and batch (build.bat) build scripts.
899 lines
32 KiB
JavaScript
899 lines
32 KiB
JavaScript
/*
|
|
* @fileoverview Main Doctrine object
|
|
* @author Yusuke Suzuki <utatane.tea@gmail.com>
|
|
* @author Dan Tao <daniel.tao@gmail.com>
|
|
* @author Andrew Eisenberg <andrew@eisenberg.as>
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
var typed,
|
|
utility,
|
|
jsdoc,
|
|
esutils,
|
|
hasOwnProperty;
|
|
|
|
esutils = require('esutils');
|
|
typed = require('./typed');
|
|
utility = require('./utility');
|
|
|
|
function sliceSource(source, index, last) {
|
|
return source.slice(index, last);
|
|
}
|
|
|
|
hasOwnProperty = (function () {
|
|
var func = Object.prototype.hasOwnProperty;
|
|
return function hasOwnProperty(obj, name) {
|
|
return func.call(obj, name);
|
|
};
|
|
}());
|
|
function shallowCopy(obj) {
|
|
var ret = {}, key;
|
|
for (key in obj) {
|
|
if (obj.hasOwnProperty(key)) {
|
|
ret[key] = obj[key];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function isASCIIAlphanumeric(ch) {
|
|
return (ch >= 0x61 /* 'a' */ && ch <= 0x7A /* 'z' */) ||
|
|
(ch >= 0x41 /* 'A' */ && ch <= 0x5A /* 'Z' */) ||
|
|
(ch >= 0x30 /* '0' */ && ch <= 0x39 /* '9' */);
|
|
}
|
|
|
|
function isParamTitle(title) {
|
|
return title === 'param' || title === 'argument' || title === 'arg';
|
|
}
|
|
|
|
function isReturnTitle(title) {
|
|
return title === 'return' || title === 'returns';
|
|
}
|
|
|
|
function isProperty(title) {
|
|
return title === 'property' || title === 'prop';
|
|
}
|
|
|
|
function isNameParameterRequired(title) {
|
|
return isParamTitle(title) || isProperty(title) ||
|
|
title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
|
|
}
|
|
|
|
function isAllowedName(title) {
|
|
return isNameParameterRequired(title) || title === 'const' || title === 'constant';
|
|
}
|
|
|
|
function isAllowedNested(title) {
|
|
return isProperty(title) || isParamTitle(title);
|
|
}
|
|
|
|
function isAllowedOptional(title) {
|
|
return isProperty(title) || isParamTitle(title);
|
|
}
|
|
|
|
function isTypeParameterRequired(title) {
|
|
return isParamTitle(title) || isReturnTitle(title) ||
|
|
title === 'define' || title === 'enum' ||
|
|
title === 'implements' || title === 'this' ||
|
|
title === 'type' || title === 'typedef' || isProperty(title);
|
|
}
|
|
|
|
// Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
|
|
// This would require changes to 'parseType'
|
|
function isAllowedType(title) {
|
|
return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
|
|
title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
|
|
title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
|
|
title === 'public' || title === 'private' || title === 'protected';
|
|
}
|
|
|
|
// A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)
|
|
var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';
|
|
|
|
var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';
|
|
|
|
function unwrapComment(doc) {
|
|
// JSDoc comment is following form
|
|
// /**
|
|
// * .......
|
|
// */
|
|
|
|
return doc.
|
|
// remove /**
|
|
replace(/^\/\*\*?/, '').
|
|
// remove */
|
|
replace(/\*\/$/, '').
|
|
// remove ' * ' at the beginning of a line
|
|
replace(new RegExp(STAR_MATCHER, 'g'), '$2').
|
|
// remove trailing whitespace
|
|
replace(/\s*$/, '');
|
|
}
|
|
|
|
/**
|
|
* Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version
|
|
* @param {string} originalSource The original wrapped comment
|
|
* @param {number} unwrappedIndex The index of a character in the unwrapped string
|
|
* @returns {number} The index of the corresponding character in the original wrapped string
|
|
*/
|
|
function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {
|
|
var replacedSource = originalSource.replace(/^\/\*\*?/, '');
|
|
var numSkippedChars = 0;
|
|
var matcher = new RegExp(STAR_MATCHER, 'g');
|
|
var match;
|
|
|
|
while ((match = matcher.exec(replacedSource))) {
|
|
numSkippedChars += match[1].length;
|
|
|
|
if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {
|
|
return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;
|
|
}
|
|
}
|
|
|
|
return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;
|
|
}
|
|
|
|
// JSDoc Tag Parser
|
|
|
|
(function (exports) {
|
|
var Rules,
|
|
index,
|
|
lineNumber,
|
|
length,
|
|
source,
|
|
originalSource,
|
|
recoverable,
|
|
sloppy,
|
|
strict;
|
|
|
|
function advance() {
|
|
var ch = source.charCodeAt(index);
|
|
index += 1;
|
|
if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D /* '\r' */ && source.charCodeAt(index) === 0x0A /* '\n' */)) {
|
|
lineNumber += 1;
|
|
}
|
|
return String.fromCharCode(ch);
|
|
}
|
|
|
|
function scanTitle() {
|
|
var title = '';
|
|
// waste '@'
|
|
advance();
|
|
|
|
while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {
|
|
title += advance();
|
|
}
|
|
|
|
return title;
|
|
}
|
|
|
|
function seekContent() {
|
|
var ch, waiting, last = index;
|
|
|
|
waiting = false;
|
|
while (last < length) {
|
|
ch = source.charCodeAt(last);
|
|
if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D /* '\r' */ && source.charCodeAt(last + 1) === 0x0A /* '\n' */)) {
|
|
waiting = true;
|
|
} else if (waiting) {
|
|
if (ch === 0x40 /* '@' */) {
|
|
break;
|
|
}
|
|
if (!esutils.code.isWhiteSpace(ch)) {
|
|
waiting = false;
|
|
}
|
|
}
|
|
last += 1;
|
|
}
|
|
return last;
|
|
}
|
|
|
|
// type expression may have nest brace, such as,
|
|
// { { ok: string } }
|
|
//
|
|
// therefore, scanning type expression with balancing braces.
|
|
function parseType(title, last, addRange) {
|
|
var ch, brace, type, startIndex, direct = false;
|
|
|
|
|
|
// search '{'
|
|
while (index < last) {
|
|
ch = source.charCodeAt(index);
|
|
if (esutils.code.isWhiteSpace(ch)) {
|
|
advance();
|
|
} else if (ch === 0x7B /* '{' */) {
|
|
advance();
|
|
break;
|
|
} else {
|
|
// this is direct pattern
|
|
direct = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (direct) {
|
|
return null;
|
|
}
|
|
|
|
// type expression { is found
|
|
brace = 1;
|
|
type = '';
|
|
while (index < last) {
|
|
ch = source.charCodeAt(index);
|
|
if (esutils.code.isLineTerminator(ch)) {
|
|
advance();
|
|
} else {
|
|
if (ch === 0x7D /* '}' */) {
|
|
brace -= 1;
|
|
if (brace === 0) {
|
|
advance();
|
|
break;
|
|
}
|
|
} else if (ch === 0x7B /* '{' */) {
|
|
brace += 1;
|
|
}
|
|
if (type === '') {
|
|
startIndex = index;
|
|
}
|
|
type += advance();
|
|
}
|
|
}
|
|
|
|
if (brace !== 0) {
|
|
// braces is not balanced
|
|
return utility.throwError('Braces are not balanced');
|
|
}
|
|
|
|
if (isAllowedOptional(title)) {
|
|
return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});
|
|
}
|
|
|
|
return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});
|
|
}
|
|
|
|
function scanIdentifier(last) {
|
|
var identifier;
|
|
if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {
|
|
return null;
|
|
}
|
|
identifier = advance();
|
|
while (index < last && esutils.code.isIdentifierPartES5(source.charCodeAt(index))) {
|
|
identifier += advance();
|
|
}
|
|
return identifier;
|
|
}
|
|
|
|
function skipWhiteSpace(last) {
|
|
while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {
|
|
advance();
|
|
}
|
|
}
|
|
|
|
function parseName(last, allowBrackets, allowNestedParams) {
|
|
var name = '',
|
|
useBrackets,
|
|
insideString;
|
|
|
|
|
|
skipWhiteSpace(last);
|
|
|
|
if (index >= last) {
|
|
return null;
|
|
}
|
|
|
|
if (source.charCodeAt(index) === 0x5B /* '[' */) {
|
|
if (allowBrackets) {
|
|
useBrackets = true;
|
|
name = advance();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
name += scanIdentifier(last);
|
|
|
|
if (allowNestedParams) {
|
|
if (source.charCodeAt(index) === 0x3A /* ':' */ && (
|
|
name === 'module' ||
|
|
name === 'external' ||
|
|
name === 'event')) {
|
|
name += advance();
|
|
name += scanIdentifier(last);
|
|
|
|
}
|
|
if(source.charCodeAt(index) === 0x5B /* '[' */ && source.charCodeAt(index + 1) === 0x5D /* ']' */){
|
|
name += advance();
|
|
name += advance();
|
|
}
|
|
while (source.charCodeAt(index) === 0x2E /* '.' */ ||
|
|
source.charCodeAt(index) === 0x2F /* '/' */ ||
|
|
source.charCodeAt(index) === 0x23 /* '#' */ ||
|
|
source.charCodeAt(index) === 0x2D /* '-' */ ||
|
|
source.charCodeAt(index) === 0x7E /* '~' */) {
|
|
name += advance();
|
|
name += scanIdentifier(last);
|
|
}
|
|
}
|
|
|
|
if (useBrackets) {
|
|
skipWhiteSpace(last);
|
|
// do we have a default value for this?
|
|
if (source.charCodeAt(index) === 0x3D /* '=' */) {
|
|
// consume the '='' symbol
|
|
name += advance();
|
|
skipWhiteSpace(last);
|
|
|
|
var ch;
|
|
var bracketDepth = 1;
|
|
|
|
// scan in the default value
|
|
while (index < last) {
|
|
ch = source.charCodeAt(index);
|
|
|
|
if (esutils.code.isWhiteSpace(ch)) {
|
|
if (!insideString) {
|
|
skipWhiteSpace(last);
|
|
ch = source.charCodeAt(index);
|
|
}
|
|
}
|
|
|
|
if (ch === 0x27 /* ''' */) {
|
|
if (!insideString) {
|
|
insideString = '\'';
|
|
} else {
|
|
if (insideString === '\'') {
|
|
insideString = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ch === 0x22 /* '"' */) {
|
|
if (!insideString) {
|
|
insideString = '"';
|
|
} else {
|
|
if (insideString === '"') {
|
|
insideString = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ch === 0x5B /* '[' */) {
|
|
bracketDepth++;
|
|
} else if (ch === 0x5D /* ']' */ &&
|
|
--bracketDepth === 0) {
|
|
break;
|
|
}
|
|
|
|
name += advance();
|
|
}
|
|
}
|
|
|
|
skipWhiteSpace(last);
|
|
|
|
if (index >= last || source.charCodeAt(index) !== 0x5D /* ']' */) {
|
|
// we never found a closing ']'
|
|
return null;
|
|
}
|
|
|
|
// collect the last ']'
|
|
name += advance();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
function skipToTag() {
|
|
while (index < length && source.charCodeAt(index) !== 0x40 /* '@' */) {
|
|
advance();
|
|
}
|
|
if (index >= length) {
|
|
return false;
|
|
}
|
|
utility.assert(source.charCodeAt(index) === 0x40 /* '@' */);
|
|
return true;
|
|
}
|
|
|
|
function convertIndex(rangeIndex) {
|
|
if (source === originalSource) {
|
|
return rangeIndex;
|
|
}
|
|
return convertUnwrappedCommentIndex(originalSource, rangeIndex);
|
|
}
|
|
|
|
function TagParser(options, title) {
|
|
this._options = options;
|
|
this._title = title.toLowerCase();
|
|
this._tag = {
|
|
title: title,
|
|
description: null
|
|
};
|
|
if (this._options.lineNumbers) {
|
|
this._tag.lineNumber = lineNumber;
|
|
}
|
|
this._first = index - title.length - 1;
|
|
this._last = 0;
|
|
// space to save special information for title parsers.
|
|
this._extra = { };
|
|
}
|
|
|
|
// addError(err, ...)
|
|
TagParser.prototype.addError = function addError(errorText) {
|
|
var args = Array.prototype.slice.call(arguments, 1),
|
|
msg = errorText.replace(
|
|
/%(\d)/g,
|
|
function (whole, index) {
|
|
utility.assert(index < args.length, 'Message reference must be in range');
|
|
return args[index];
|
|
}
|
|
);
|
|
|
|
if (!this._tag.errors) {
|
|
this._tag.errors = [];
|
|
}
|
|
if (strict) {
|
|
utility.throwError(msg);
|
|
}
|
|
this._tag.errors.push(msg);
|
|
return recoverable;
|
|
};
|
|
|
|
TagParser.prototype.parseType = function () {
|
|
// type required titles
|
|
if (isTypeParameterRequired(this._title)) {
|
|
try {
|
|
this._tag.type = parseType(this._title, this._last, this._options.range);
|
|
if (!this._tag.type) {
|
|
if (!isParamTitle(this._title) && !isReturnTitle(this._title)) {
|
|
if (!this.addError('Missing or invalid tag type')) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
this._tag.type = null;
|
|
if (!this.addError(error.message)) {
|
|
return false;
|
|
}
|
|
}
|
|
} else if (isAllowedType(this._title)) {
|
|
// optional types
|
|
try {
|
|
this._tag.type = parseType(this._title, this._last, this._options.range);
|
|
} catch (e) {
|
|
//For optional types, lets drop the thrown error when we hit the end of the file
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype._parseNamePath = function (optional) {
|
|
var name;
|
|
name = parseName(this._last, sloppy && isAllowedOptional(this._title), true);
|
|
if (!name) {
|
|
if (!optional) {
|
|
if (!this.addError('Missing or invalid tag name')) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
this._tag.name = name;
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseNamePath = function () {
|
|
return this._parseNamePath(false);
|
|
};
|
|
|
|
TagParser.prototype.parseNamePathOptional = function () {
|
|
return this._parseNamePath(true);
|
|
};
|
|
|
|
|
|
TagParser.prototype.parseName = function () {
|
|
var assign, name;
|
|
|
|
// param, property requires name
|
|
if (isAllowedName(this._title)) {
|
|
this._tag.name = parseName(this._last, sloppy && isAllowedOptional(this._title), isAllowedNested(this._title));
|
|
if (!this._tag.name) {
|
|
if (!isNameParameterRequired(this._title)) {
|
|
return true;
|
|
}
|
|
|
|
// it's possible the name has already been parsed but interpreted as a type
|
|
// it's also possible this is a sloppy declaration, in which case it will be
|
|
// fixed at the end
|
|
if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
|
|
this._extra.name = this._tag.type;
|
|
this._tag.name = this._tag.type.name;
|
|
this._tag.type = null;
|
|
} else {
|
|
if (!this.addError('Missing or invalid tag name')) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
name = this._tag.name;
|
|
if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
|
|
// extract the default value if there is one
|
|
// example: @param {string} [somebody=John Doe] description
|
|
assign = name.substring(1, name.length - 1).split('=');
|
|
if (assign.length > 1) {
|
|
this._tag['default'] = assign.slice(1).join('=');
|
|
}
|
|
this._tag.name = assign[0];
|
|
|
|
// convert to an optional type
|
|
if (this._tag.type && this._tag.type.type !== 'OptionalType') {
|
|
this._tag.type = {
|
|
type: 'OptionalType',
|
|
expression: this._tag.type
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseDescription = function parseDescription() {
|
|
var description = sliceSource(source, index, this._last).trim();
|
|
if (description) {
|
|
if ((/^-\s+/).test(description)) {
|
|
description = description.substring(2);
|
|
}
|
|
this._tag.description = description;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseCaption = function parseDescription() {
|
|
var description = sliceSource(source, index, this._last).trim();
|
|
var captionStartTag = '<caption>';
|
|
var captionEndTag = '</caption>';
|
|
var captionStart = description.indexOf(captionStartTag);
|
|
var captionEnd = description.indexOf(captionEndTag);
|
|
if (captionStart >= 0 && captionEnd >= 0) {
|
|
this._tag.caption = description.substring(
|
|
captionStart + captionStartTag.length, captionEnd).trim();
|
|
this._tag.description = description.substring(captionEnd + captionEndTag.length).trim();
|
|
} else {
|
|
this._tag.description = description;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseKind = function parseKind() {
|
|
var kind, kinds;
|
|
kinds = {
|
|
'class': true,
|
|
'constant': true,
|
|
'event': true,
|
|
'external': true,
|
|
'file': true,
|
|
'function': true,
|
|
'member': true,
|
|
'mixin': true,
|
|
'module': true,
|
|
'namespace': true,
|
|
'typedef': true
|
|
};
|
|
kind = sliceSource(source, index, this._last).trim();
|
|
this._tag.kind = kind;
|
|
if (!hasOwnProperty(kinds, kind)) {
|
|
if (!this.addError('Invalid kind name \'%0\'', kind)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseAccess = function parseAccess() {
|
|
var access;
|
|
access = sliceSource(source, index, this._last).trim();
|
|
this._tag.access = access;
|
|
if (access !== 'private' && access !== 'protected' && access !== 'public') {
|
|
if (!this.addError('Invalid access name \'%0\'', access)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.parseThis = function parseThis() {
|
|
// this name may be a name expression (e.g. {foo.bar}),
|
|
// an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)
|
|
var value = sliceSource(source, index, this._last).trim();
|
|
if (value && value.charAt(0) === '{') {
|
|
var gotType = this.parseType();
|
|
if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {
|
|
this._tag.name = this._tag.type.name;
|
|
return true;
|
|
} else {
|
|
return this.addError('Invalid name for this');
|
|
}
|
|
} else {
|
|
return this.parseNamePath();
|
|
}
|
|
};
|
|
|
|
TagParser.prototype.parseVariation = function parseVariation() {
|
|
var variation, text;
|
|
text = sliceSource(source, index, this._last).trim();
|
|
variation = parseFloat(text, 10);
|
|
this._tag.variation = variation;
|
|
if (isNaN(variation)) {
|
|
if (!this.addError('Invalid variation \'%0\'', text)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.ensureEnd = function () {
|
|
var shouldBeEmpty = sliceSource(source, index, this._last).trim();
|
|
if (shouldBeEmpty) {
|
|
if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
TagParser.prototype.epilogue = function epilogue() {
|
|
var description;
|
|
|
|
description = this._tag.description;
|
|
// un-fix potentially sloppy declaration
|
|
if (isAllowedOptional(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
|
|
this._tag.type = this._extra.name;
|
|
if (!this._tag.name) {
|
|
this._tag.name = undefined;
|
|
}
|
|
|
|
if (!sloppy) {
|
|
if (!this.addError('Missing or invalid tag name')) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
Rules = {
|
|
// http://usejsdoc.org/tags-access.html
|
|
'access': ['parseAccess'],
|
|
// http://usejsdoc.org/tags-alias.html
|
|
'alias': ['parseNamePath', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-augments.html
|
|
'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-constructor.html
|
|
'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// Synonym: http://usejsdoc.org/tags-constructor.html
|
|
'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// Synonym: http://usejsdoc.org/tags-extends.html
|
|
'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-example.html
|
|
'example': ['parseCaption'],
|
|
// http://usejsdoc.org/tags-deprecated.html
|
|
'deprecated': ['parseDescription'],
|
|
// http://usejsdoc.org/tags-global.html
|
|
'global': ['ensureEnd'],
|
|
// http://usejsdoc.org/tags-inner.html
|
|
'inner': ['ensureEnd'],
|
|
// http://usejsdoc.org/tags-instance.html
|
|
'instance': ['ensureEnd'],
|
|
// http://usejsdoc.org/tags-kind.html
|
|
'kind': ['parseKind'],
|
|
// http://usejsdoc.org/tags-mixes.html
|
|
'mixes': ['parseNamePath', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-mixin.html
|
|
'mixin': ['parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-member.html
|
|
'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-method.html
|
|
'method': ['parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-module.html
|
|
'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// Synonym: http://usejsdoc.org/tags-method.html
|
|
'func': ['parseNamePathOptional', 'ensureEnd'],
|
|
// Synonym: http://usejsdoc.org/tags-method.html
|
|
'function': ['parseNamePathOptional', 'ensureEnd'],
|
|
// Synonym: http://usejsdoc.org/tags-member.html
|
|
'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-name.html
|
|
'name': ['parseNamePath', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-namespace.html
|
|
'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-private.html
|
|
'private': ['parseType', 'parseDescription'],
|
|
// http://usejsdoc.org/tags-protected.html
|
|
'protected': ['parseType', 'parseDescription'],
|
|
// http://usejsdoc.org/tags-public.html
|
|
'public': ['parseType', 'parseDescription'],
|
|
// http://usejsdoc.org/tags-readonly.html
|
|
'readonly': ['ensureEnd'],
|
|
// http://usejsdoc.org/tags-requires.html
|
|
'requires': ['parseNamePath', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-since.html
|
|
'since': ['parseDescription'],
|
|
// http://usejsdoc.org/tags-static.html
|
|
'static': ['ensureEnd'],
|
|
// http://usejsdoc.org/tags-summary.html
|
|
'summary': ['parseDescription'],
|
|
// http://usejsdoc.org/tags-this.html
|
|
'this': ['parseThis', 'ensureEnd'],
|
|
// http://usejsdoc.org/tags-todo.html
|
|
'todo': ['parseDescription'],
|
|
// http://usejsdoc.org/tags-typedef.html
|
|
'typedef': ['parseType', 'parseNamePathOptional'],
|
|
// http://usejsdoc.org/tags-variation.html
|
|
'variation': ['parseVariation'],
|
|
// http://usejsdoc.org/tags-version.html
|
|
'version': ['parseDescription']
|
|
};
|
|
|
|
TagParser.prototype.parse = function parse() {
|
|
var i, iz, sequences, method;
|
|
|
|
|
|
// empty title
|
|
if (!this._title) {
|
|
if (!this.addError('Missing or invalid title')) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Seek to content last index.
|
|
this._last = seekContent(this._title);
|
|
|
|
if (this._options.range) {
|
|
this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);
|
|
}
|
|
|
|
if (hasOwnProperty(Rules, this._title)) {
|
|
sequences = Rules[this._title];
|
|
} else {
|
|
// default sequences
|
|
sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
|
|
}
|
|
|
|
for (i = 0, iz = sequences.length; i < iz; ++i) {
|
|
method = sequences[i];
|
|
if (!this[method]()) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return this._tag;
|
|
};
|
|
|
|
function parseTag(options) {
|
|
var title, parser, tag;
|
|
|
|
// skip to tag
|
|
if (!skipToTag()) {
|
|
return null;
|
|
}
|
|
|
|
// scan title
|
|
title = scanTitle();
|
|
|
|
// construct tag parser
|
|
parser = new TagParser(options, title);
|
|
tag = parser.parse();
|
|
|
|
// Seek global index to end of this tag.
|
|
while (index < parser._last) {
|
|
advance();
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
//
|
|
// Parse JSDoc
|
|
//
|
|
|
|
function scanJSDocDescription(preserveWhitespace) {
|
|
var description = '', ch, atAllowed;
|
|
|
|
atAllowed = true;
|
|
while (index < length) {
|
|
ch = source.charCodeAt(index);
|
|
|
|
if (atAllowed && ch === 0x40 /* '@' */) {
|
|
break;
|
|
}
|
|
|
|
if (esutils.code.isLineTerminator(ch)) {
|
|
atAllowed = true;
|
|
} else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {
|
|
atAllowed = false;
|
|
}
|
|
|
|
description += advance();
|
|
}
|
|
|
|
return preserveWhitespace ? description : description.trim();
|
|
}
|
|
|
|
function parse(comment, options) {
|
|
var tags = [], tag, description, interestingTags, i, iz;
|
|
|
|
if (options === undefined) {
|
|
options = {};
|
|
}
|
|
|
|
if (typeof options.unwrap === 'boolean' && options.unwrap) {
|
|
source = unwrapComment(comment);
|
|
} else {
|
|
source = comment;
|
|
}
|
|
|
|
originalSource = comment;
|
|
|
|
// array of relevant tags
|
|
if (options.tags) {
|
|
if (Array.isArray(options.tags)) {
|
|
interestingTags = { };
|
|
for (i = 0, iz = options.tags.length; i < iz; i++) {
|
|
if (typeof options.tags[i] === 'string') {
|
|
interestingTags[options.tags[i]] = true;
|
|
} else {
|
|
utility.throwError('Invalid "tags" parameter: ' + options.tags);
|
|
}
|
|
}
|
|
} else {
|
|
utility.throwError('Invalid "tags" parameter: ' + options.tags);
|
|
}
|
|
}
|
|
|
|
length = source.length;
|
|
index = 0;
|
|
lineNumber = 0;
|
|
recoverable = options.recoverable;
|
|
sloppy = options.sloppy;
|
|
strict = options.strict;
|
|
|
|
description = scanJSDocDescription(options.preserveWhitespace);
|
|
|
|
while (true) {
|
|
tag = parseTag(options);
|
|
if (!tag) {
|
|
break;
|
|
}
|
|
if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
|
|
tags.push(tag);
|
|
}
|
|
}
|
|
|
|
return {
|
|
description: description,
|
|
tags: tags
|
|
};
|
|
}
|
|
exports.parse = parse;
|
|
}(jsdoc = {}));
|
|
|
|
exports.version = utility.VERSION;
|
|
exports.parse = jsdoc.parse;
|
|
exports.parseType = typed.parseType;
|
|
exports.parseParamType = typed.parseParamType;
|
|
exports.unwrapComment = unwrapComment;
|
|
exports.Syntax = shallowCopy(typed.Syntax);
|
|
exports.Error = utility.DoctrineError;
|
|
exports.type = {
|
|
Syntax: exports.Syntax,
|
|
parseType: typed.parseType,
|
|
parseParamType: typed.parseParamType,
|
|
stringify: typed.stringify
|
|
};
|
|
}());
|
|
/* vim: set sw=4 ts=4 et tw=80 : */
|