Skip to content

Instantly share code, notes, and snippets.

@venning
Created March 23, 2015 20:09
Show Gist options
  • Save venning/55173b5e5f80aea39995 to your computer and use it in GitHub Desktop.
Save venning/55173b5e5f80aea39995 to your computer and use it in GitHub Desktop.
lodash: automated test for iteratee/predicate in documentation
#!/usr/bin/env node
/*
* much of this was taken from:
* https://bitbucket.org/ariya/missing-doc/src/master/missing-doc.js
*/
'use strict';
var _ = require('lodash'),
fs = require('fs'),
esprima = require('esprima'),
estraverse = require('estraverse'),
doctrine = require('doctrine'),
indentString = require('indent-string');
////////////////////////
//// MAIN ROUTINE ////
////////////////////////
// globals because I'm lazy
var total = 0,
successes = 0;
// lodash source file as command-line argument
buildAndWalkTree(process.argv[2]);
console.log('\n' + successes + ' out of ' + total + ' examples succeeeded.')
///////////////////
//// WORKERS ////
///////////////////
function buildAndWalkTree (filename) {
var content, tree;
try {
content = fs.readFileSync(filename, 'utf-8');
tree = esprima.parse(content, { attachComment: true, loc: true });
// walk the tree
estraverse.traverse(tree, { enter: workNode });
} catch (e) {
console.error(e.toString());
process.exit(1);
}
}
function workNode (node) {
if (node.type === 'Identifier') {
// just a duplicate from something else
return;
}
if (node.leadingComments && node.id && node.id.leadingComments) {
// this should never happen, but it's worth getting a warning if it does
console.error(node.id.name + ' has leadingComments in two places in the AST');
process.exit(1);
}
var leadingComments = node.leadingComments || (node.id && node.id.leadingComments);
var name = node.name || (node.id && node.id.name);
_.forEach(leadingComments, _.partial(workComment, name));
}
function workComment (name, comment) {
var doctrineOpts = {
unwrap: true, // pulls out the leading `*`s
recoverable: true, // allows for badly-formed JSDoc
sloppy: true // correctly parses square-bracketed optional parameters
};
var data = doctrine.parse(comment.value, doctrineOpts);
// some functions have @name annotations that should take precedence
name = _.result(_.find(data.tags, { title: 'name' }), 'name') || name;
// do the work
var err = checkParams(data);
if (err) {
console.log(name + '\n' + indentString(err, ' ', 4));
} else {
++successes;
}
++total;
}
// returns error
function checkParams (data) {
var desc = data.description;
var paramNames = _.pluck(_.where(data.tags, { title: 'param' }), 'name');
var hasIteratee = _.contains(paramNames, 'iteratee');
var hasPredicate = _.contains(paramNames, 'predicate');
// shouldn't happen, but warn if so
if (hasIteratee && hasPredicate) {
return 'ERROR: BOTH iteratee AND predicate PARAMETERS';
}
if (hasIteratee) {
if (desc.match(/predicate/i)) {
return 'USES iteratee, DESCRIBES predicate';
}
} else if (hasPredicate) {
if (desc.match(/iteratee/i)) {
return 'USES predicate, DESCRIBES iteratee';
}
}
}
{
"dependencies": {
"lodash": "~3.5.0",
"estraverse": "~3.1.0",
"esprima": "~2.1.0",
"doctrine": "~0.6.4",
"indent-string": "~1.2.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment