var esprima = require('esprima'); var fs = require('fs'); var skip = /^\s+\*/gm; var mapName = function(elm){ return elm.name; }; var findReturns = function(expr){ var returns = [] expr.body.forEach(function(expr){ if(expr.type === 'ReturnStatement'){ if(expr.argument.type === 'Literal'){ returns.push(expr.argument.value); } else if(expr.argument.type === 'ObjectExpression'){ returns.push('Object'); } else if(expr.argument.type === 'ArrayExpression'){ returns.push('Array'); } else if(expr.argument.type === 'FunctionExpression'){ returns.push('Function'); } } }); return returns; }; var findRaises = function(expr){ var raises = [] expr.body.forEach(function(expr){ if(expr.type === 'ThrowStatement'){ if(expr.argument.type === 'NewExpression'){ raises.push(expr.argument.callee.name); } } }); return raises; }; var parseExpressions = function(expr){ var expressions = {}; var _parse = function(expr){ if(!expr){ return; } else if(expr.length && typeof expr.forEach === 'function'){ return expr.forEach(_parse); } if(expr.type === 'FunctionExpression'){ expressions[expr.loc.start.line] = { name: expr.id, params: expr.params.map(mapName), returns: findReturns(expr.body), raises: findRaises(expr.body), }; _parse(expr.body); } else if(expr.type === 'FunctionDeclaration'){ expressions[expr.loc.start.line] = { name: expr.id.name, params: expr.params.map(mapName), returns: findReturns(expr.body), raises: findRaises(expr.body), }; _parse(expr.body); } else if(expr.type === 'AssignmentExpression' && expr.right.type === 'FunctionExpression'){ var data = {}; if(expr.left.type === 'MemberExpression'){ data.name = expr.left.property.name; if(expr.left.object.type === 'MemberExpression'){ if(expr.left.object.property.name === 'prototype'){ data.class = expr.left.object.object.name; } } } if(expr.right.type === 'FunctionExpression'){ data.params = expr.right.params.map(mapName); data.returns = findReturns(expr.right.body); data.raises = findRaises(expr.right.body); } expressions[expr.loc.start.line] = data; } else if(expr.type === 'VariableDeclarator' && expr.init.type === 'FunctionExpression'){ expressions[expr.loc.start.line] = { name: expr.id.name, params: expr.init.params.map(mapName), returns: findReturns(expr.init.body), raises: findRaises(expr.init.body), }; } else { _parse(expr.expression || expr.callee || expr.body || expr.declarations); } }; _parse(expr); return expressions; }; module.exports.parseDataFile = function(filename, options){ return module.exports.parseData(fs.readFileSync(filename, 'utf-8'), options); }; module.exports.parseData = function(contents, options){ options = options || {}; var tolerant = (options.tolerant === undefined)? true: options.tolerant; var parsed = esprima.parse(contents, {comment: true, loc: true, tolerant: tolerant}); var expressions = parseExpressions(parsed); var results = []; parsed.comments.forEach(function(comment){ if(comment.type === 'Block'){ var body = comment.value; body = body.replace(skip, ''); results.push({ doc: body, data: expressions[comment.loc.end.line + 1], }); } }); return results; };