123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- /*
- * @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 : */
|