docs/source/api.js
/*!
* Module dependencies
*/
var fs = require('fs');
var link = require('../helpers/linktype');
var hl = require('highlight.js');
var md = require('markdown');
module.exports = {
docs: []
, github: 'https://github.com/archangel-irk/storage/blob/'
, title: 'API docs'
};
var out = module.exports.docs;
var docs = fs.readFileSync(__dirname + '/_docs', 'utf8');
parse(docs);
order(out);
function parse (docs) {
docs.split(/^### /gm).forEach(function (chunk) {
if (!(chunk = chunk.trim())) return;
chunk = chunk.split(/^([^\n]+)\n/);
var fulltitle = chunk[1];
if (!fulltitle || !(fulltitle = fulltitle.trim()))
throw new Error('missing title');
var title = fulltitle.replace(/^lib\//, '');
var json = JSON.parse(chunk[2]);
var props = [];
var methods = [];
var statics = [];
var constructor = null;
json.forEach(function (comment) {
if (comment.description) {
comment.description.full = comment.description.full.replace(/'/g, "'");
comment.description.summary = comment.description.summary.replace(/'/g, "'");
comment.description.body = comment.description.body.replace(/'/gmi, "\'");
highlight(comment.description);
}
var prop = false;
comment.params = [];
comment.see = [];
var i = comment.tags.length;
while (i--) {
var tag = comment.tags[i];
switch (tag.type) {
case 'property':
prop = true;
comment.ctx || (comment.ctx = {});
comment.ctx.name = comment.ctx.name || tag.string || tag.types[0];
props.unshift(comment);
break;
case 'method':
prop = false;
comment.ctx || (comment.ctx = {});
comment.ctx.name || (comment.ctx.name = tag.string);
comment.ctx.type = 'method';
comment.code = '';
break;
case 'memberOf':
prop = false;
comment.ctx || (comment.ctx = {});
comment.ctx.constructor = tag.parent;
break;
case 'static':
prop = false;
comment.ctx || (comment.ctx = {});
comment.ctx.name = tag.string;
comment.ctx.type = 'static';
break;
case 'receiver':
prop = false;
comment.ctx || (comment.ctx = {});
comment.ctx.receiver = tag.string;
break;
case 'constructor':
prop = false;
comment.ctx || (comment.ctx = {});
comment.ctx.name || (comment.ctx.name = tag.string);
comment.ctx.type = 'function';
comment.code = '';
break;
case 'inherits':
if (/http/.test(tag.string)) {
var result = tag.string.split(' ');
var href = result.pop();
var title = result.join(' ');
comment.inherits = '<a href="'
+ href
+ '" title="' + title + '">' + title + '</a>';
} else {
comment.inherits = link(tag.string);
}
comment.tags.splice(i, 1);
break;
case 'param':
comment.params.unshift(tag);
comment.tags.splice(i, 1);
break;
case 'return':
comment.return = tag;
comment.tags.splice(i, 1);
break;
case 'see':
if (tag.local) {
var parts = tag.local.split(' ');
if (1 === parts.length) {
tag.url = link.type(parts[0]);
tag.title = parts[0];
} else {
tag.url = parts.pop();
tag.title = parts.join(' ');
}
}
comment.see.unshift(tag);
comment.tags.splice(i, 1);
break;
case 'event':
var str = tag.string.replace(/\\n/g, '\n');
tag.string = md.parse(str).replace(/\n/g, '\\n').replace(/'/g, ''');
comment.events || (comment.events = []);
comment.events.unshift(tag);
comment.tags.splice(i, 1);
}
}
if (!prop) {
methods.push(comment);
}
});
methods = methods.filter(ignored);
props = props.filter(ignored);
function ignored (method) {
if (method.ignore) return false;
return true;
}
if (0 === methods.length + props.length) return;
// add constructor to properties too
methods.some(function (method) {
if (method.ctx && 'method' == method.ctx.type && method.ctx.hasOwnProperty('constructor')) {
props.forEach(function (prop) {
prop.ctx.constructor = method.ctx.constructor;
});
return true;
}
return false;
});
var len = methods.length;
while (len--) {
method = methods[len];
if (method.ctx && method.ctx.receiver) {
var stat = methods.splice(len, 1)[0];
statics.unshift(stat);
}
}
out.push({
title: title
, fulltitle: fulltitle
, cleantitle: title.replace(/[\.\/#]/g, '-')
, methods: methods
, props: props
, statics: statics
, hasPublic: hasPublic(methods, props, statics)
});
});
}
function hasPublic () {
for (var i = 0; i < arguments.length; ++i) {
var arr = arguments[i];
for (var j = 0; j < arr.length; ++j) {
var item = arr[j];
if (!item.ignore && !item.isPrivate) return true;
}
}
return false;
}
// add "class='language'" to our <pre><code> elements
function highlight (o) {
o.full = fix(o.full);
o.summary = fix(o.summary);
o.body = fix(o.body);
}
function fix (str) {
return str.replace(/(<pre><code>)([^<]+)(<\/code)/gm, function (_, $1, $2, $3) {
// parse out the ```language
var code = /^(?:`{3}([^\n]+)\n)?([\s\S]*)/gm.exec($2);
if ('js' == code[1] || !code[1]) {
code[1] = 'javascript';
}
return $1
+ hl.highlight(code[1], code[2]).value.trim()
+ $3;
});
}
function order (docs) {
var sortByCtxName = function (a, b) {
if (!a.ctx) {
console.error('missing a ctx', a);
}
if (!b.ctx) {
console.error('missing b ctx', b);
}
if ( a.ctx && !a.ctx.name ){
console.log( 'missing a ctx name' );
console.log( a );
}
return a.ctx.name.localeCompare(b.ctx.name);
};
// want index first
for (var i = 0; i < docs.length; ++i) {
var doc = docs[i];
if ('index.js' === doc.title) {
docs.unshift(docs.splice(i, 1)[0]);
} else if ('collection.js' === doc.title) {
docs.push(docs.splice(i, 1)[0]);
}
// sort methods, statics and properties alphabetically
doc.methods.sort(sortByCtxName);
doc.statics.sort(sortByCtxName);
doc.props.sort(sortByCtxName);
}
}