crojsdoc
Version:
A documentation generator for JavaScript and CoffeeScript
767 lines (741 loc) • 25.6 kB
JavaScript
// Generated by CoffeeScript 1.8.0
(function() {
var Collector, collect, dox, inflect, is_test_mode, markdown, _;
_ = require('lodash');
dox = require('./dox');
inflect = require('inflect');
markdown = require('marked');
is_test_mode = process.env.NODE_ENV === 'test';
Collector = (function() {
function Collector(contents, options) {
this.contents = contents;
this.options = options != null ? options : {};
this.result = {
project_title: this.options.title || 'croquis-jsdoc',
ids: {},
classes: {},
guides: [],
pages: {},
restapis: {},
features: [],
files: []
};
}
Collector.prototype.addGuide = function(file, data) {
var id, item;
id = file.substr(0, file.length - 3);
file = file.substr(0, file.length - 8).replace(/\//g, '.');
item = {
name: inflect.humanize(inflect.underscore(file)),
filename: 'guides/' + file,
content: markdown(data)
};
this.result.guides.push(item);
return this.result.ids[id] = item;
};
Collector.prototype.addFeature = function(file, data) {
var feature, namespace;
file = file.substr(0, file.length - 8);
namespace = '';
file = file.replace(/(.*)\//, function(_, $1) {
namespace = $1 + '/';
return '';
});
feature = '';
data = data.replace(/Feature: (.*)/, function(_, $1) {
feature = $1;
return '';
});
return this.result.features.push({
name: namespace + file,
namespace: namespace,
filename: 'features/' + namespace.replace(/\//g, '.') + file,
feature: feature,
content: data
});
};
Collector.prototype.addFile = function(file, data) {
var namespace;
namespace = '';
file = file.replace(/(.*)\//, function(_, $1) {
namespace = $1 + '/';
return '';
});
return this.result.files.push({
name: namespace + file,
namespace: namespace,
filename: 'files/' + namespace.replace(/\//g, '.') + file,
content: data
});
};
Collector.prototype.processParamFlags = function(tag) {
var pos;
if (tag.name[0] === '[' && tag.name[tag.name.length - 1] === ']') {
tag.name = tag.name.substr(1, tag.name.length - 2);
if ((pos = tag.name.indexOf('=')) >= 0) {
tag.default_value = tag.name.substr(pos + 1);
tag.name = tag.name.substr(0, pos);
}
tag.optional = true;
}
if (tag.name.substr(0, 1) === '+') {
tag.name = tag.name.substr(1);
tag.addable = true;
}
if (tag.name.substr(0, 1) === '-') {
tag.name = tag.name.substr(1);
tag.excludable = true;
}
return tag;
};
Collector.prototype.findParam = function(params, name) {
var found, param, _i, _len;
for (_i = 0, _len = params.length; _i < _len; _i++) {
param = params[_i];
if (param.name === name) {
return param;
}
if (param.params) {
found = this.findParam(param.params, name);
if (found) {
return found;
}
}
}
};
Collector.prototype.makeNested = function(comment, targetName) {
var i, match, param, parentParam, _results;
i = comment[targetName].length;
_results = [];
while (i-- > 0) {
param = comment[targetName][i];
if (match = param.name.match(/\[?([^=]*)\.([^\]]*)\]?/)) {
parentParam = this.findParam(comment[targetName], match[1]);
if (parentParam) {
comment[targetName].splice(i, 1);
parentParam[targetName] = parentParam[targetName] || [];
param.name = match[2];
_results.push(parentParam[targetName].unshift(param));
} else {
_results.push(void 0);
}
} else {
_results.push(void 0);
}
}
return _results;
};
Collector.prototype.applyMarkdown = function(str) {
str = str.replace(/#\\#/g, '##');
return markdown(str);
};
Collector.prototype.classifyComments = function(comments) {
var current_class, current_module;
current_class = void 0;
current_module = void 0;
return comments.forEach((function(_this) {
return function(comment) {
var i, id, last, seperator, tag, _i, _len, _ref;
comment.ctx || (comment.ctx = {});
comment.params = [];
comment.returnprops = [];
comment.throws = [];
comment.resterrors = [];
comment.sees = [];
comment.reverse_sees = [];
comment.todos = [];
comment["extends"] = [];
comment.subclasses = [];
comment.uses = [];
comment.usedbys = [];
comment.properties = [];
comment.examples = [];
if (comment.ctx.type === 'property' || comment.ctx.type === 'method') {
id = comment.ctx.string.replace('()', '');
} else {
id = comment.ctx.name;
}
comment.ctx.fullname = id;
comment.namespace = '';
if (comment.ctx.type === 'property' || comment.ctx.type === 'method') {
if (comment.ctx.cons != null) {
comment["static"] = false;
comment.ctx.class_name = comment.ctx.cons;
} else if (comment.ctx.receiver != null) {
comment["static"] = true;
comment.ctx.class_name = comment.ctx.receiver;
}
}
last = 0;
_ref = comment.tags;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
tag = _ref[i];
if (tag.type === '') {
comment.tags[last].string += "\n" + tag.string;
continue;
}
last = i;
switch (tag.type) {
case 'page':
case 'restapi':
case 'class':
comment.ctx.type = tag.type;
if (tag.string) {
comment.ctx.name = tag.string;
comment.ctx.fullname = id = comment.ctx.name;
}
break;
case 'module':
comment.ctx.type = 'class';
comment.is_module = true;
if (tag.string) {
comment.ctx.name = tag.string;
comment.ctx.fullname = id = comment.ctx.name;
}
comment.code = null;
break;
case 'memberof':
if (/(::|#|\.prototype)$/.test(tag.parent)) {
comment["static"] = false;
comment.ctx.class_name = tag.parent.replace(/(::|#|\.prototype)$/, '');
} else {
comment["static"] = true;
comment.ctx.class_name = tag.parent;
}
break;
case 'namespace':
comment.namespace = tag.string ? tag.string + '.' : '';
break;
case 'property':
case 'method':
comment.ctx.type = tag.type;
if (tag.string) {
comment.ctx.name = tag.string;
}
break;
case 'static':
comment["static"] = true;
break;
case 'private':
comment.isPrivate = true;
break;
case 'abstract':
comment.abstract = true;
break;
case 'async':
comment.async = true;
break;
case 'promise':
comment.return_promise = true;
break;
case 'nodejscallback':
comment.return_nodejscallback = true;
break;
case 'chainable':
comment.chainable = true;
break;
case 'param':
case 'return':
case 'returnprop':
case 'throws':
case 'resterror':
case 'see':
case 'extends':
case 'todo':
case 'type':
case 'api':
case 'uses':
case 'override':
break;
default:
console.log("Unknown tag : " + tag.type + " in " + comment.defined_in);
}
}
if (comment.ctx.class_name) {
if (comment.ctx.type === 'function') {
comment.ctx.type = 'method';
} else if (comment.ctx.type === 'declaration') {
comment.ctx.type = 'property';
}
seperator = comment["static"] ? '.' : '::';
id = comment.ctx.class_name + seperator + comment.ctx.name;
comment.ctx.fullname = comment.ctx.class_name.replace(/.*[\./](\w+)/, '$1') + seperator + comment.ctx.name;
}
if (comment.ctx.type === 'class') {
current_class = comment;
if (comment.is_module) {
current_module = comment;
}
}
if ((comment.ctx.type === 'property' || comment.ctx.type === 'method') && !comment.namespace) {
if (current_class) {
comment.namespace = current_class.namespace;
}
if (current_module && !comment.ctx.class_name) {
comment.ctx.class_name = current_module.ctx.name;
}
}
if (id) {
comment.id = id;
if (_this.result.ids.hasOwnProperty(id)) {
_this.result.ids[id] = 'DUPLICATED ENTRY';
} else {
_this.result.ids[id] = comment;
}
if (comment.namespace && _this.result.ids.hasOwnProperty(comment.namespace + id)) {
_this.result.ids[comment.namespace + id] = 'DUPLICATED ENTRY';
} else {
_this.result.ids[comment.namespace + id] = comment;
}
comment.html_id = (comment.namespace + id).replace(/[^A-Za-z0-9_]/g, '_');
}
switch (comment.ctx.type) {
case 'class':
comment.ctx.name = comment.namespace + comment.ctx.name;
comment.ctx.fullname = comment.namespace + comment.ctx.fullname;
_this.result.classes[comment.ctx.name] = comment;
if (comment.is_module) {
return comment.filename = 'modules/' + comment.ctx.name.replace(/\//g, '.');
} else {
return comment.filename = 'classes/' + comment.ctx.name.replace(/\//g, '.');
}
break;
case 'property':
case 'method':
comment.ctx.class_name = comment.namespace + comment.ctx.class_name;
return comment.filename = 'classes/' + comment.ctx.class_name.replace(/\//g, '.');
case 'page':
return comment.filename = 'pages';
case 'restapi':
return comment.filename = 'restapis';
}
};
})(this));
};
Collector.prototype.getComments = function(type, path, file, data) {
var comments, namespace;
if (type === 'coffeescript') {
comments = dox.parseCommentsCoffee(data, {
raw: true
});
} else if (type === 'javascript') {
comments = dox.parseComments(data, {
raw: true
});
} else if (type === 'page') {
namespace = '';
file = file.substr(0, file.length - 3).replace(/[^A-Za-z0-9]*Page$/, '');
file = file.replace(/(.*)\//, function(_, $1) {
namespace = $1;
return '';
});
comments = [
{
description: {
summary: '',
body: data,
full: ''
},
tags: [
{
type: 'page',
string: file
}, {
type: 'namespace',
string: namespace
}
]
}
];
}
if (comments == null) {
return;
}
comments = comments.filter(function(comment) {
var _ref;
return comment.description.full || comment.description.summary || comment.description.body || ((_ref = comment.tags) != null ? _ref.length : void 0) > 0;
});
comments.forEach((function(_this) {
return function(comment) {
return comment.defined_in = path;
};
})(this));
this.classifyComments(comments);
return comments;
};
Collector.prototype.processComments = function(comments) {
return comments.forEach((function(_this) {
return function(comment) {
var callback_params, class_comment, class_name, desc, i, str, tag, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
desc = comment.description;
if (desc) {
desc.full = _this.applyMarkdown(desc.full);
desc.summary = _this.applyMarkdown(desc.summary);
desc.body = _this.applyMarkdown(desc.body);
}
_ref = comment.tags;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
tag = _ref[_i];
switch (tag.type) {
case 'param':
tag = _this.processParamFlags(tag);
_ref1 = tag.types;
for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {
type = _ref1[i];
tag.types[i] = type;
}
tag.description = tag.description;
comment.params.push(tag);
break;
case 'return':
_ref2 = tag.types;
for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) {
type = _ref2[i];
tag.types[i] = type;
}
tag.description = tag.description;
comment["return"] = tag;
break;
case 'returnprop':
tag = dox.parseTag('@param ' + tag.string);
tag = _this.processParamFlags(tag);
_ref3 = tag.types;
for (i = _l = 0, _len3 = _ref3.length; _l < _len3; i = ++_l) {
type = _ref3[i];
tag.types[i] = type;
}
tag.description = tag.description;
comment.returnprops.push(tag);
break;
case 'throws':
if (/{([^}]+)}\s*(.*)/.exec(tag.string)) {
comment.throws.push({
message: RegExp.$1,
description: RegExp.$2
});
} else {
comment.throws.push({
message: tag.string,
description: ''
});
}
break;
case 'resterror':
if (/{(\d+)\/([A-Za-z0-9_ ]+)}\s*(.*)/.exec(tag.string)) {
comment.resterrors.push({
code: RegExp.$1,
message: RegExp.$2,
description: RegExp.$3
});
}
break;
case 'see':
str = tag.local || tag.url;
comment.sees.push(str);
break;
case 'todo':
comment.todos.push(tag.string);
break;
case 'extends':
comment["extends"].push(tag.string);
if ((_ref4 = _this.result.ids[tag.string]) != null) {
_ref4.subclasses.push(comment.ctx.name);
}
break;
case 'uses':
comment.uses.push(tag.string);
if ((_ref5 = _this.result.ids[tag.string]) != null) {
_ref5.usedbys.push(comment.ctx.name);
}
break;
case 'type':
_ref6 = tag.types;
for (i = _m = 0, _len4 = _ref6.length; _m < _len4; i = ++_m) {
type = _ref6[i];
tag.types[i] = type;
}
comment.types = tag.types;
break;
case 'example':
comment.examples.push(tag);
break;
case 'override':
if (_this.result.ids[tag.string] && _this.result.ids[tag.string] !== 'DUPLICATED ENTRY') {
comment.override = _this.result.ids[tag.string];
}
comment.override_link = tag.string;
}
}
if (comment.ctx.type === 'class') {
if (/^class +\w+ +extends +([\w\.]+)/.exec(comment.class_code)) {
comment["extends"].push(RegExp.$1);
if ((_ref7 = _this.result.ids[RegExp.$1]) != null) {
_ref7.subclasses.push(comment.ctx.name);
}
}
}
_this.makeNested(comment, 'params');
_this.makeNested(comment, 'returnprops');
if (comment.return_nodejscallback) {
callback_params = [
{
name: 'error',
types: ['Error'],
description: 'See throws'
}
];
if (comment["return"]) {
callback_params.push({
name: 'result',
types: comment["return"].types,
description: 'See returns'
});
}
comment.params.push({
name: 'callback',
types: ['Function'],
optional: comment.return_promise,
description: 'NodeJS style\'s callback',
params: callback_params
});
}
if (comment.chainable && !comment["return"]) {
comment["return"] = {
types: [comment.ctx.class_name],
description: 'this'
};
}
switch (comment.ctx.type) {
case 'property':
case 'method':
class_name = comment.ctx.class_name;
if (class_name && (class_comment = _this.result.classes[class_name])) {
if (comment.ctx.is_coffeescript_constructor) {
class_comment.code = comment.code;
class_comment.line_number = comment.line_number;
return class_comment.params = comment.params;
} else {
class_comment.properties.push(comment);
if (class_comment.is_module) {
return comment.filename = comment.filename.replace('classes/', 'modules/');
}
}
}
break;
case 'page':
return _this.result.pages[comment.ctx.name] = comment;
case 'restapi':
return _this.result.restapis[comment.ctx.name] = comment;
}
};
})(this));
};
Collector.prototype.refineResult = function() {
var result;
result = this.result;
result.classes = Object.keys(result.classes).sort(function(a, b) {
var a_ns, b_ns;
a_ns = result.classes[a].namespace;
b_ns = result.classes[b].namespace;
if (a_ns < b_ns) {
return -1;
}
if (a_ns > b_ns) {
return 1;
}
if (a < b) {
return -1;
} else {
return 1;
}
}).map(function(name) {
return result.classes[name];
});
result.pages = Object.keys(result.pages).sort(function(a, b) {
var a_ns, b_ns;
a_ns = result.pages[a].namespace;
b_ns = result.pages[b].namespace;
if (a_ns < b_ns) {
return -1;
}
if (a_ns > b_ns) {
return 1;
}
if (a < b) {
return -1;
} else {
return 1;
}
}).map(function(name) {
return result.pages[name];
});
result.restapis = Object.keys(result.restapis).sort(function(a, b) {
var a_ns, b_ns;
a_ns = result.restapis[a].namespace;
b_ns = result.restapis[b].namespace;
if (a_ns < b_ns) {
return -1;
}
if (a_ns > b_ns) {
return 1;
}
a = a.replace(/([A-Z]+) \/(.*)/, '-$2 $1');
b = b.replace(/([A-Z]+) \/(.*)/, '-$2 $1');
if (a < b) {
return -1;
} else {
return 1;
}
}).map(function(name) {
return result.restapis[name];
});
result.guides = result.guides.sort(function(a, b) {
if (a.name < b.name) {
return -1;
} else {
return 1;
}
});
result.features = result.features.sort(function(a, b) {
if (a.name < b.name) {
return -1;
} else {
return 1;
}
});
result.files = result.files.sort(function(a, b) {
var a_ns, b_ns;
a_ns = a.namespace;
b_ns = b.namespace;
if (a_ns < b_ns) {
return -1;
}
if (a_ns > b_ns) {
return 1;
}
if (a.name < b.name) {
return -1;
} else {
return 1;
}
});
result.classes.forEach(function(klass) {
var property, _i, _len, _ref, _results;
klass.properties.sort(function(a, b) {
if (a.ctx.name < b.ctx.name) {
return -1;
} else {
return 1;
}
});
_ref = klass.properties;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
property = _ref[_i];
_results.push(property.ctx = _.pick(property.ctx, 'type', 'name', 'fullname'));
}
return _results;
});
result.modules = result.classes.filter(function(klass) {
return klass.is_module;
});
return result.classes = result.classes.filter(function(klass) {
return !klass.is_module;
});
};
Collector.prototype.getType = function(file) {
if (/\.coffee$/.test(file)) {
return 'coffeescript';
} else if (/\.js$/.test(file)) {
return 'javascript';
} else if (/Page\.md$/.test(file)) {
return 'page';
} else if (/Guide\.md$/.test(file)) {
return 'guide';
} else if (/\.feature$/.test(file)) {
return 'feature';
} else if (file === 'README') {
return 'readme';
} else {
return 'unknown';
}
};
Collector.prototype.makeReverseSeeAlso = function(comments) {
var comment, me, other, see, _i, _j, _len, _len1, _ref, _ref1, _ref2;
for (_i = 0, _len = comments.length; _i < _len; _i++) {
comment = comments[_i];
_ref = comment.sees;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
see = _ref[_j];
other = this.result.ids[see];
if (other && other !== 'DUPLICATED ENTRY') {
me = this.result.ids[comment.id];
if (me && me === 'DUPLICATED ENTRY') {
if ((_ref1 = other.reverse_sees) != null) {
_ref1.push(comment.namespace + comment.id);
}
} else {
if ((_ref2 = other.reverse_sees) != null) {
_ref2.push(comment.id);
}
}
}
}
}
};
Collector.prototype.run = function() {
var all_comments, comments, data, file, file_count_read, path, type, _i, _len, _ref, _ref1;
all_comments = [];
file_count_read = 0;
_ref = this.contents;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
_ref1 = _ref[_i], path = _ref1.path, file = _ref1.file, data = _ref1.data;
type = this.getType(file);
switch (type) {
case 'guide':
this.addGuide(file, data);
break;
case 'feature':
this.addFeature(file, data);
break;
case 'coffeescript':
case 'javascript':
case 'page':
comments = this.getComments(type, path, file, data);
if (comments != null) {
[].push.apply(all_comments, comments);
}
break;
case 'readme':
this.result.readme = markdown(data);
}
if (type === 'coffeescript' || type === 'javascript') {
this.addFile(file, data);
}
file_count_read++;
if (!(this.options.quite || is_test_mode)) {
console.log(file + ' is processed');
}
}
if (!is_test_mode) {
console.log('Total ' + file_count_read + ' files processed');
}
this.processComments(all_comments);
if (this.options.reverse_see_also) {
this.makeReverseSeeAlso(all_comments);
}
if (!this.options.files) {
this.result.files = [];
}
return this.refineResult();
};
return Collector;
})();
collect = function(contents, options) {
var collector;
collector = new Collector(contents, options);
collector.run();
return collector.result;
};
module.exports = collect;
}).call(this);