savvy-js
Version:
Savvy - Style sheet documentation tool
227 lines (198 loc) • 7.09 kB
JavaScript
(function(){
'use strict';
var lineExpression = /\n/gm,
nameExpression = /^\s*[a-zA-z_-]*/,
commentExpression = /\/\*{2}([\s\S]+?)\*\//gm,
argumentExpression = /\*\s{0,3}@/g,
statementTypes = ['class', 'module', 'submodule', 'stateof', 'parent', 'comment'];
/**
* Extract all documentation comment blocks from a file and return them as array.
*
* CommentBlock is an object that contains the line number, index, abd the comment string:
*
* {
* line : number, // The line number in the style document where the comments begins
* index : number, // The character index in the style document where the comments begins
* comment : string // The comment itself.
* }
*
* @method extractComments
* @param {string} document - The document (style file content) in which to look for comments.
* @returns {commentBlock[]} a list of comments that were extracted from the document
*/
function extractComments(document){
var comments = [],
lines = extractLines(document),
lastLineIndex = 0,
match;
while(match = commentExpression.exec(document)) {
while(lines[lastLineIndex] <= match.index){
lastLineIndex++;
}
comments.push({
line : lastLineIndex + 1,
index : match.index,
comment : match[1]
});
}
return comments;
}
/**
* Remove the opening and closing of a comment
*
* @method trimComment
* @param {string} comment
*/
function trimComment(comment){
var closingIndex = comment.lastIndexOf('*/');
closingIndex = closingIndex === -1 ? comment.length - 1 : closingIndex;
comment = comment.substring(0, closingIndex).replace('/**', '');
return comment;
}
/**
* Get array of line indexes from a document.
*
* @method extractLines
* @param {string} document - The document (style file content) in which to look for lines.
* @returns {Array} a list of line break indexes.
* @private
*/
function extractLines(document){
var lines = [],
line;
while(line = lineExpression.exec(document)){
lines.push(line.index);
}
return lines;
}
/**
* Parse a comment into an object
*
* @method parseComment
* @type {function( comment : commentBlock )}
* @returns []
*/
function parseComment(commentBlock){
var result = {
description : [],
line : commentBlock.line
},
rawArguments = trimComment(commentBlock.comment).split(argumentExpression),
statementType,
statement;
rawArguments.forEach(function(statementString){
statement = parseStatement(statementString);
if(statement.type === 'comment'){
result.description = [].concat(result.description, statement.description);
}
else{
statementType = statement.type;
delete statement.type;
result[statementType] = statement;
}
});
return result;
}
/**
* Parse a statement within a comment. For example, convert
*
* @class myStuff
*
* To
*
* { type : 'class', name : 'myStuff', description : [] }
*
* @param {string} statement
*/
function parseStatement(statement){
var lines = statement.split(lineExpression),
currentParagraph = null,
result = {
type : null,
name : null,
description : []
};
function setStatementType(line, index){
if(index === 0){
result.type = findStatementType(line);
if(result.type !== 'comment' || line.indexOf(result.type) > -1) {
line = line.substring(line.indexOf(result.type) + result.type.length);
}
}
return line;
}
function setStatementName(line, index){
var match;
if(index === 0 && result.type !== 'comment'){
match = line.match(nameExpression);
if(match){
line = line.substring(match[0].length);
result.name = match[0].trim();
}
}
return line;
}
function setStatementDescription(line, index){
var description = {
type : 'text',
text : ''
};
line = removeLeadingAstrix(line);
if(line) {
if(line.indexOf(' ') === 0){
description.type = 'code';
description.text = line;
}
else{
description.text = line.trim();
}
result.description.push(description);
}
else if(index > 0 && result.description.length > 0){
description.type = 'paragraph';
result.description.push(description);
}
}
// remove the astrix from the beginning of the line
function removeLeadingAstrix(line){
line = line.replace(/^(\s)*\*+/, '');
// remove spaces, unless its at least 4 spaces then it's a code description
if(line.indexOf(' ') !== 0){
line = line.replace(/^(\s)*/, '');
}
return line;
}
lines.forEach(function(line, index){
if(index === 0) {
line = removeLeadingAstrix(line);
line = setStatementType(line, index);
line = setStatementName(line, index);
}
setStatementDescription(line, index);
});
return result;
}
/**
* Take a line, and find out if it starts with a known statement.
*
* @method findStatementType
* @param {string} line - a line of text from the comment
*/
function findStatementType(line){
var result = 'comment',
tokenExpression;
statementTypes.forEach(function(statementType){
tokenExpression = new RegExp('^[@]?' + statementType + '\\s', 'i');
if(line.match(tokenExpression)){
result = statementType;
return false; // break forEach loop
}
});
return result;
}
module.exports = {
extractComments : extractComments,
parseComment : parseComment,
parseStatement : parseStatement
};
}());