不要怂,就是干,撸起袖子干!

Commit 22f0f451 by Jan Aagaard Meier

Restructured docs generator

1 parent 576b9555
...@@ -16,7 +16,6 @@ charset = utf-8 ...@@ -16,7 +16,6 @@ charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
indent_size = 4 indent_size = 4
...@@ -90,3 +90,9 @@ coveralls: sqlite-cover mysql-cover postgres-cover postgres-native-cover mariadb ...@@ -90,3 +90,9 @@ coveralls: sqlite-cover mysql-cover postgres-cover postgres-native-cover mariadb
codeclimate: sqlite-cover mysql-cover postgres-cover postgres-native-cover mariadb-cover merge-coverage codeclimate-send codeclimate: sqlite-cover mysql-cover postgres-cover postgres-native-cover mariadb-cover merge-coverage codeclimate-send
.PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test .PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test
api-docs:
node docs/docs-generator.js
clean:
node docs/docs-generator.js --clean
"use strict";
var dox = require('dox')
, program = require('commander')
, fs = require('fs')
, path = require('path')
, git = require('git')
, _ = require('lodash');
program
.version('0.0.2')
.option('-f, --file [file]', 'Process a single file', '')
.option('-a, --all', 'Process all files, generate index etc. (default if no options are specified')
.option('-c, --clean', 'Remove all generated markdown and HTML files')
.option('-o --out [dir]', '', path.dirname(__filename))
.parse(process.argv);
if (program.clean) {
fs.readdirSync('docs/').forEach(function (file) {
if (file !== 'index.md' && path.extname(file) === '.md') {
fs.unlinkSync ('docs/' + file);
}
});
return;
}
var files;
if (program.file) {
files = [{file: program.file, output: 'tmp'}];
} else {
files = [
{file:'lib/errors.js', output: 'errors'},
{file:'lib/sequelize.js', output: 'sequelize'},
{file:'lib/instance.js', output: 'instance'},
{file:'lib/model.js', output: 'model'},
{file:'lib/hooks.js', output: 'hooks'},
{file:'lib/associations/mixin.js', output: 'associations'},
{file:'lib/promise.js', output: 'promise'},
{file:'lib/transaction.js', output: 'transaction'},
{file:'lib/data-types.js', output: 'datatypes'}
];
}
var Comment = function(data, file) {
this.data = data;
this.file = file;
this.string = '';
};
Comment.prototype.getTag = function(tagName) {
return _.find(this.data.tags, function (tag) {
return tag.type === tagName;
});
};
Comment.prototype.getTags = function(tagName) {
return _.where(this.data.tags, function (tag) {
return tag.type === tagName;
});
};
Comment.prototype.hasTag = function(tagName) {
return this.getTag(tagName) !== undefined;
};
Comment.prototype.getName = function () {
var tag;
tag = this.getTag('name');
if (tag) {
return tag.string;
}
tag = this.getTag('class');
if (tag) {
return tag.string;
}
tag = this.getTag('property');
if (tag) {
return tag.types[0];
}
tag = this.getTag('method');
if (tag) {
return tag.string;
}
return this.data.ctx.name;
};
Comment.prototype.getParams = function () {
if (this.isProperty()) {
return '';
}
// Only show params without a dot in them (dots means attributes of object, so no need to clutter the signature too much)
var params = [] ;
this.getTags('param').forEach(function (paramTag) {
if (paramTag.name.indexOf('.') === -1) {
params.push(paramTag.name);
}
});
return '(' + params.join(', ') + ')';
};
Comment.prototype.isProperty = function () {
return !this.hasTag('method') && this.data.ctx && this.data.ctx.type === 'property';
};
Comment.prototype.putString = function(str) {
this.string += str;
};
Comment.prototype.putLine = function(str) {
str = str || '';
this.putString(str + "\n");
};
Comment.prototype.putLines = function(lines) {
lines.forEach(function (line) {
this.putLine(line);
}, this);
};
Comment.prototype.toString = function () {
return this.string + "\n";
};
['class', 'mixin', 'constructor'].forEach(function (prop) {
Comment.prototype['is' + prop.charAt(0).toUpperCase() + prop.slice(1)] = function () {
return this.hasTag(prop);
};
});
Comment.prototype.githubLink = function() {
return 'https://github.com/sequelize/sequelize/blob/' + Comment.commit + '/' + this.file + '#L' + this.data.codeStart;
};
Comment.concatTypes = function (types, convertEntities) {
if (convertEntities === undefined) {
convertEntities = true;
}
var type = types.join('|');
if (type.indexOf('<') !== -1 && type.indexOf('>') === -1) {
// If the string is Array<something|somethingelse> the closing > disappears...
type += '>';
}
if (convertEntities) {
// Convert a couple of things to their HTML-entities
// The spacing around | is intentional, in order to introduce some linebreaks in the params table
type = type.replace('|', ' &#124; ')
.replace('>', '&gt;')
.replace('<', '&lt;');
}
return type;
};
var parseComments = function (comments, file) {
var output = ''
, comment
, name
, returns
, mixes
, deprecated
, see
, params
, extend
, aliases;
comments.forEach(function (data) {
if (data.tags.length) {
comment = new Comment(data, file);
name = comment.getName();
comment.putLine('<a name="' + name.toLowerCase() + '"></a>');
if (comment.isClass()) {
comment.putLine('# Class ' + name);
} else if (comment.isMixin()) {
comment.putLine('# Mixin ' + name);
} else if (comment.isConstructor()) {
comment.putLine('## `new ' + name + comment.getParams() + '`');
} else {
comment.putString('## `' + name + comment.getParams() + '`');
if (comment.hasTag('return')) {
returns = comment.getTag('return');
var returnType = Comment.concatTypes(returns.types, false); // We don't convert HTML entities since tihs is displayed in a literal block
comment.putString(' -> `' + returnType + '`');
}
comment.putLine();
}
comment.putLine('[View code](' + comment.githubLink() + ')');
comment.putLine(comment.data.description.full);
if ((mixes = comment.getTags('mixes')).length) {
comment.putLine('### Mixes:');
mixes.forEach(function (mixin) {
comment.putLine('* ' + mixin.string);
});
}
if (deprecated = comment.getTag('deprecated')) {
comment.putLine('**Deprecated** ' + deprecated.string);
}
if ((see = comment.getTags('see')).length) {
comment.putLine();
comment.putLine('**See:**');
comment.putLine();
var link;
see.forEach(function (see) {
if (see.local) {
link = see.local.match(/{(.*?(?:|#.*?))}/)[1];
comment.putLine('* [' + link + '](' + link.toLowerCase() + ')');
} else {
comment.putLine('* [' + see.title | see.url + '](' + see.url + ')');
}
});
comment.putLine();
}
if (comment.hasTag('param')) {
params = comment.getTags('param');
comment.putLines([
'',
'**Params:**',
'',
'| Name | Type | Description |',
'| ---- | ---- | ----------- |'
]);
var type;
params.forEach(function (param) {
type = Comment.concatTypes(param.types);
comment.putLine('| ' + param.name + ' | ' + type + ' | ' + param.description + ' |');
});
comment.putLine();
}
if (returns && returns.description) {
comment.putLine('__Returns:__ ' + returns.description);
}
if ((aliases = comment.getTags('alias')).length) {
comment.putLine('__Aliases:__ ' + aliases.map(function (a) {
return a.string;
}).join(', '));
}
if (extend = comment.getTag('extends')) {
comment.putLine();
comment.putLine('__Extends:__ ' + extend.otherClass);
}
comment.putLine();
comment.putLine('***');
output += comment.toString();
}
});
output += '_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on <a href="irc://irc.freenode.net/#sequelizejs">IRC</a>, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [dox](https://github.com/tj/dox)_';
return output;
};
var code, obj, output, path;
new git.Repo(path.dirname(__filename) + '/..', function (err, repo) {
repo.head(function (err, status) {
Comment.commit = status.commit;
files.forEach(function (file) {
fs.readFile(file.file, function (err, code) {
obj = dox.parseComments(code.toString(), { raw: true});
path = program.out + '/' + file.output + '.md';
console.log(path)
var output = parseComments(obj, file.file);
fs.writeFile(path, output);
});
});
});
});
"use strict";
var markdox = require('markdox')
, program = require('commander')
, fs = require('fs')
, _ = require('lodash');
program
.version('0.0.1')
.option('-f, --file [file]', 'Process a single file', '')
.option('-a, --all', 'Process all files, generate index etc. (default if no options are specified')
.option('-c, --clean', 'Remove all generated markdown and HTML files')
.option('--html', 'Generate html files from the markdown ones (requires manual installation of the github-flavored-markdown package')
.parse(process.argv);
if (program.clean) {
fs.readdirSync('docs/').forEach(function (file) {
if (file.indexOf('.ejs') === -1 && file.indexOf('.js') === -1) {
fs.unlinkSync ('docs/' + file);
}
});
return ;
}
if (program.html) {
var ghm = require('github-flavored-markdown');
}
var getTag = function(tags, tagName) {
return _.find(tags, function (tag) {
return tag.type === tagName;
});
};
var getTags = function(tags, tagName) {
return _.where(tags, function (tag) {
return tag.type === tagName;
});
};
var options = {
formatter: function (docfile) {
docfile = markdox.defaultFormatter(docfile);
docfile.members = [];
docfile.javadoc.forEach(function(javadoc){
javadoc.isConstructor = getTag(javadoc.raw.tags, 'constructor') !== undefined;
javadoc.isMixin = getTag(javadoc.raw.tags, 'mixin') !== undefined;
javadoc.isProperty = getTag(javadoc.raw.tags, 'property') !== undefined;
javadoc.private = getTag(javadoc.raw.tags, 'private') !== undefined;
javadoc.since = getTag(javadoc.raw.tags, 'since');
javadoc.extends = getTag(javadoc.raw.tags, 'extends');
// Find constructor tags
// Only show params without a dot in them (dots means attributes of object, so no need to clutter the signature too much)
var params = [] ;
javadoc.paramTags.forEach(function (paramTag) {
if (paramTag.name.indexOf('.') === -1) {
params.push(paramTag.name);
}
});
javadoc.paramStr = (javadoc.isMethod || javadoc.isFunction) ? '(' + params.join(', ') + ')' : '';
// Convert | to &#124; to be able to use github flavored md tables
if (javadoc.paramTags) {
javadoc.paramTags.forEach(function (paramTag) {
paramTag.joinedTypes = paramTag.joinedTypes.replace(/\|/g, '&#124;');
});
}
// Handle aliases
javadoc.aliases = getTags(javadoc.raw.tags, 'alias').map(function (a) {
return a.string;
}).join(', ');
// Handle deprecation text
if (javadoc.deprecated) {
var deprecation = getTag(javadoc.raw.tags, 'deprecated');
javadoc.deprecated = deprecation.string;
}
// Handle linking in comments
javadoc.see = getTags(javadoc.raw.tags, 'see');
javadoc.see.forEach(function (see, i, collection) {
collection[i] = {};
if (see.local) {
collection[i].external = false;
if (see.local.indexOf('{') === 0){
var _see = see.local.split('}');
_see[0] = _see[0].substring(1);
collection[i].url = 'API-Reference-' + _see[0];
collection[i].text = see.local.replace(/{|}/g, '');
} else {
collection[i].url = false;
collection[i].text = see.local;
}
} else {
see.external = true;
see.text = see.url;
collection[i] = see;
}
});
// Set a name for properties
if (!javadoc.name) {
var property = getTag(javadoc.raw.tags, 'property');
if (property) {
javadoc.name = property.types[0];
}
}
if (javadoc.isMixin) {
javadoc.name = getTag(javadoc.raw.tags, 'mixin').string;
}
javadoc.mixes = getTags(javadoc.raw.tags, 'mixes').map(function (mix) {
return {
text: mix.string,
link: (mix.string.indexOf('www') !== -1 || mix.string.indexOf('http') !== -1) ? mix.string: 'API-Reference-' + mix.string
};
});
if (!javadoc.isClass) {
if (!javadoc.isProperty) {
docfile.members.push({
text: javadoc.name + javadoc.paramStr,
link: '#' + javadoc.name
});
} else {
docfile.members.push({
text: javadoc.name,
link: '#' + javadoc.name
});
}
}
});
return docfile;
},
template: 'docs/output.md.ejs'
};
var files;
if (program.file) {
files = [{file: program.file, output: 'tmp'}];
} else {
files = [
{file:'lib/errors.js', output: 'API-Reference-Errors'},
{file:'lib/sequelize.js', output: 'API-Reference-Sequelize'},
{file:'lib/instance.js', output: 'API-Reference-Instance'},
{file:'lib/model.js', output: 'API-Reference-Model'},
{file:'lib/query-chainer.js', output: 'API-Reference-QueryChainer'},
{file:'lib/hooks.js', output: 'API-Reference-Hooks'},
{file:'lib/associations/mixin.js', output: 'API-Reference-Associations'},
{file:'lib/promise.js', output: 'API-Reference-Promise'},
{file:'lib/transaction.js', output: 'API-Reference-Transaction'},
{file:'lib/data-types.js', output: 'API-Reference-DataTypes'}
];
}
files.forEach(function (file) {
var opts = _.clone(options)
, output = 'docs/' + file.output + '.md';
opts.output = output;
markdox.process(file.file, opts, function(){
if (program.html) {
var md = fs.readFileSync(output).toString();
fs.writeFileSync(output.replace('md', 'html'), ghm.parse(md));
}
});
});
<? docfiles.forEach(function(doc) { ?>
<? doc.javadoc.forEach(function(comment) { ?>
<? if (comment.name && comment.private !== true) { -?>
<? if (!comment.ignore) { ?>
<a name="<?= comment.name ?>" />
<? if (comment.isConstructor) { ?>
### `new <?= comment.name ?><?= comment.paramStr ?>`
<? } else if (comment.isMixin) { ?>
### Mixin <?= comment.name ?>
<? } else if (comment.isClass) { ?>
### Class <?= comment.name ?>
<? } else { ?>
#### `<?= comment.name ?><?= comment.paramStr ?>` <? if (comment.returnTags.length > 0) { ?> -> `<?= comment.returnTags[0].joinedTypes ?>` <? } ?>
<? } ?>
<? } ?>
<?= comment.description ?>
<? if (comment.mixes.length) { ?>
### Mixes:
<? comment.mixes.forEach(function (mix) { -?>
* [<?=mix.text ?>](<?=mix.link ?>)
<? }) ?>
<? } ?>
<? if (comment.isClass) { ?>
### Members:
<? doc.members.forEach(function (member) { -?>
* [<?= member.text ?>](<?= member.link ?>)
<? }) -?>
<? } ?>
<? if (comment.deprecated) { ?>
**Deprecated** <?- comment.deprecated ?>
<? } ?>
<? if (comment.version) { ?>
Version: <?= comment.version ?>
<? } ?>
<? if (comment.see.length) { ?>See:<? } ?>
<? comment.see.forEach(function (see) { -?>
<? if (see.url !== false) { -?>
* [<?= see.text ?>](<?= see.url ?>)
<? } else { -?>
* <?= see.text ?>
<? } -?>
<? }) -?>
<? if (comment.paramTags.length > 0) { ?>
##### Params:
| Name | Type | Description |
| ---- | ---- | ----------- |
<? comment.paramTags.forEach(function(paramTag) { -?>
| <?= paramTag.name ?> | <?= paramTag.joinedTypes ?> | <?= paramTag.description ?> |
<? }) ?>
<? } ?>
<? if (comment.returnTags.length > 0 && comment.returnTags[0].description.length > 0) { ?>
__Returns:__ <?= comment.returnTags[0].description ?>
<? } ?>
<? if (comment.aliases) { ?>
__Aliases:__ *<?= comment.aliases ?>*
<? } ?>
<? if (comment.since) { ?>
__Since:__ *<?= comment.since.string ?>*
<? } ?>
<? if (comment.extends) { ?>
__Extends:__ *<?= comment.extends.otherClass ?>*
<? } ?>
======
<? } ?>
<? }) ?>
_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on <a href="irc://irc.freenode.net/#sequelizejs">IRC</a>, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [markdox](https://github.com/cbou/markdox)_
_This documentation was automagically created on <?= new Date().toString() ?>_
<? }) ?>
...@@ -131,6 +131,7 @@ var singleLinked = function (Type) { ...@@ -131,6 +131,7 @@ var singleLinked = function (Type) {
* *
* All methods return a promise * All methods return a promise
* *
* @method hasOne
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
...@@ -155,6 +156,7 @@ Mixin.hasOne = singleLinked(HasOne); ...@@ -155,6 +156,7 @@ Mixin.hasOne = singleLinked(HasOne);
* *
* All methods return a promise * All methods return a promise
* *
* @method belongsTo
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
...@@ -231,7 +233,7 @@ Mixin.belongsTo = singleLinked(BelongsTo); ...@@ -231,7 +233,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* @param {Model|string|object} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it. * @param {Model|string|object} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it.
* @param {Model} [options.through.model] The model used to join both sides of the N:M association. * @param {Model} [options.through.model] The model used to join both sides of the N:M association.
* @param {object} [options.through.scope] A key/value set that will be used for association create and find defaults on the through model. (Remember to add the attributes to the through model) * @param {object} [options.through.scope] A key/value set that will be used for association create and find defaults on the through model. (Remember to add the attributes to the through model)
* @param {boolean} [options.through.unique=true] If true a unique key will be generated from the foreign keys used (might want to turn this off and create specific unique keys when using scopes) * @param {boolean} [options.through.unique=true] If true a unique key will be generated from the foreign keys used (might want to turn this off and create specific unique keys when using scopes)
* @param {string|object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the pluralized name of target * @param {string|object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the pluralized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the colum. Defaults to the name of source + primary key of source * @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the colum. Defaults to the name of source + primary key of source
* @param {object} [options.scope] A key/value set that will be used for association create and find defaults on the target. (sqlite not supported for N:M) * @param {object} [options.scope] A key/value set that will be used for association create and find defaults on the target. (sqlite not supported for N:M)
......
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
"istanbul": "~0.3.0", "istanbul": "~0.3.0",
"async": "~0.9.0", "async": "~0.9.0",
"coffee-script": "~1.7.1", "coffee-script": "~1.7.1",
"markdox": "0.1.4",
"sinon-chai": "~2.6.0", "sinon-chai": "~2.6.0",
"dox": "0.5.0",
"jshint": ">=2.4.2" "jshint": ">=2.4.2"
}, },
"keywords": [ "keywords": [
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!