7
Parse comments in the given string of js.
exports.parseComments = function(js, options){
options = options || {};
var comments = []
, raw = options.raw
, comment
, buf = ''
, ignore
, within
, code;
for (var i = 0, len = js.length; i < len; ++i) {
// start comment
if (!within && '/' == js[i] && '*' == js[i+1]) {
// code following previous comment
if (buf.trim().length) {
comment = comments[comments.length - 1];
if(comment) {
comment.code = code = buf.trim();
comment.ctx = exports.parseCodeContext(code);
}
buf = '';
}
i += 2;
within = true;
ignore = '!' == js[i];
// end comment
} else if (within && '*' == js[i] && '/' == js[i+1]) {
i += 2;
buf = buf.replace(/^ *\* ?/gm, '');
var comment = exports.parseComment(buf, options);
comment.ignore = ignore;
comments.push(comment);
within = ignore = false;
buf = '';
// buffer comment or code
} else {
buf += js[i];
}
}
// trailing code
if (buf.trim().length) {
console.log(comments.length);
comment = comments[comments.length - 1];
code = buf.trim();
comment.code = code;
comment.ctx = exports.parseCodeContext(code);
}
return comments;
};Parse the given comment str.
The comment object returned contains the following
tags array of tag objectsdescription the first line of the commentbody lines following the descriptioncontent both the description and the bodyisPrivate true when "@api private" is usedexports.parseComment = function(str, options) {
str = str.trim();
options = options || {};
var comment = { tags: [] }
, raw = options.raw
, description = {};
// parse comment body
description.full = str.split('\n@')[0].replace(/^([A-Z][\w ]+):$/gm, '## $1');
description.summary = description.full.split('\n\n')[0];
description.body = description.full.split('\n\n').slice(1).join('\n\n');
comment.description = description;
// parse tags
if (~str.indexOf('\n@')) {
var tags = '@' + str.split('\n@').slice(1).join('\n@');
comment.tags = tags.split('\n').map(exports.parseTag);
comment.isPrivate = comment.tags.some(function(tag){
return 'api' == tag.type && 'private' == tag.visibility;
})
}
// markdown
if (!raw) {
description.full = markdown(description.full);
description.summary = markdown(description.summary);
description.body = markdown(description.body);
}
return comment;
}Parse tag string "@param {Array} name description" etc.
exports.parseTag = function(str) {
var tag = {}
, parts = str.split(/ +/)
, type = tag.type = parts.shift().replace('@', '');
switch (type) {
case 'param':
tag.types = exports.parseTagTypes(parts.shift());
tag.name = parts.shift() || '';
tag.description = parts.join(' ');
break;
case 'return':
tag.types = exports.parseTagTypes(parts.shift());
tag.description = parts.join(' ');
break;
case 'see':
if (~str.indexOf('http')) {
tag.title = parts.length > 1
? parts.shift()
: '';
tag.url = parts.join(' ');
} else {
tag.local = parts.join(' ');
}
case 'api':
tag.visibility = parts.shift();
break;
case 'type':
tag.types = exports.parseTagTypes(parts.shift());
break;
case 'memberOf':
tag.parent = parts.shift();
break;
case 'augments':
tag.otherClass = parts.shift();
break;
case 'borrows':
tag.otherMemberName = parts.join(' ').split(' as ')[0];
tag.thisMemberName = parts.join(' ').split(' as ')[1];
break;
default:
tag.string = parts.join(' ');
break;
}
return tag;
}Parse tag type string "{Array|Object}" etc.
exports.parseTagTypes = function(str) {
return str
.replace(/[{}]/g, '')
.split(/ *[|,\/] */);
};Parse the context from the given str of js.
This method attempts to discover the context
for the comment based on it's code. Currently
supports:
exports.parseCodeContext = function(str){
var str = str.split('\n')[0];
// function statement
if (/^function (\w+) *\(/.exec(str)) {
return {
type: 'function'
, name: RegExp.$1
, string: RegExp.$1 + '()'
};
// function expression
} else if (/^var *(\w+) *= *function/.exec(str)) {
return {
type: 'function'
, name: RegExp.$1
, string: RegExp.$1 + '()'
};
// prototype method
} else if (/^(\w+)\.prototype\.(\w+) *= *function/.exec(str)) {
return {
type: 'method'
, constructor: RegExp.$1
, name: RegExp.$2
, string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()'
};
// prototype property
} else if (/^(\w+)\.prototype\.(\w+) *= *([^\n;]+)/.exec(str)) {
return {
type: 'property'
, constructor: RegExp.$1
, name: RegExp.$2
, value: RegExp.$3
, string: RegExp.$1 + '.prototype' + RegExp.$2
};
// method
} else if (/^(\w+)\.(\w+) *= *function/.exec(str)) {
return {
type: 'method'
, receiver: RegExp.$1
, name: RegExp.$2
, string: RegExp.$1 + '.' + RegExp.$2 + '()'
};
// property
} else if (/^(\w+)\.(\w+) *= *([^\n;]+)/.exec(str)) {
return {
type: 'property'
, receiver: RegExp.$1
, name: RegExp.$2
, value: RegExp.$3
, string: RegExp.$1 + '.' + RegExp.$2
};
// declaration
} else if (/^var +(\w+) *= *([^\n;]+)/.exec(str)) {
return {
type: 'declaration'
, name: RegExp.$1
, value: RegExp.$2
, string: RegExp.$1
};
}
};
version
exports.version
Library version.