Add grunt task to generate d.ts with comments

Add "tsdocs" grunt task to generate d.ts files with comments.
This commit is contained in:
vulvulune 2015-01-20 17:18:56 +01:00
parent 6d967b6545
commit df6b946ceb
10 changed files with 30933 additions and 2 deletions

View file

@ -22,4 +22,5 @@ module.exports = function (grunt) {
grunt.registerTask('docs', [ 'clean:docs', 'pixidoc', 'builddoc', 'replace:docs', 'clean:out']);
grunt.registerTask('tsdocs', ['pixidoc', 'exportdocjson', 'buildtsdoc:pixi', 'buildtsdoc:phaser', 'replace:phasertsdefheader', 'clean:out']);
};

View file

@ -43,6 +43,7 @@
"load-grunt-config": "~0.7.2",
"yuidocjs": "^0.3.50",
"jsdoc": "~3.3.0-alpha10",
"grunt-jsdoc": "~0.5.7"
"grunt-jsdoc": "~0.5.7",
"typescript-services": "~0.1.5"
}
}

View file

@ -0,0 +1,113 @@
/**
@overview Export the jsdoc comments in a json file
@version 1.0
*/
'use strict';
function findClass(parentNode, className)
{
var elements =
parentNode.classes
.filter(function (element) {
return (element.name === className);
});
return elements[0];
}
function graft2(parentNode, childNodes) {
if (!parentNode.classes) {
parentNode.classes = [];
}
childNodes.forEach(function (element, index) {
if (element.kind === 'class') {
var thisClass = {
'name': element.longname,
'description': element.classdesc || element.description || '',
'constructor': {
'name': element.name,
'description': element.description || '',
'parameters': []
},
'functions': [],
'members': []
};
if (element.params) {
for (i = 0, len = element.params.length; i < len; i++) {
thisClass.constructor.parameters.push({
'name': element.params[i].name,
'type': element.params[i].type ? (element.params[i].type.names.length === 1 ? element.params[i].type.names[0] : element.params[i].type.names) : '',
'description': element.params[i].description || '',
'default': element.params[i].defaultvalue || '',
'optional': typeof element.params[i].optional === 'boolean' ? element.params[i].optional : '',
'nullable': typeof element.params[i].nullable === 'boolean' ? element.params[i].nullable : ''
});
}
}
parentNode.classes.push(thisClass);
}
else if (element.kind === 'function')
{
var parentClass = findClass(parentNode, element.memberof);
var thisFunction = {
'name': element.name,
'description': element.description || '',
'parameters': []
};
if (parentClass != null) {
parentClass.functions.push(thisFunction);
if (element.returns) {
thisFunction.returns = {
'type': element.returns[0].type ? (element.returns[0].type.names.length === 1 ? element.returns[0].type.names[0] : element.returns[0].type.names) : '',
'description': element.returns[0].description || ''
};
}
if (element.params) {
for (i = 0, len = element.params.length; i < len; i++) {
thisFunction.parameters.push({
'name': element.params[i].name,
'type': element.params[i].type ? (element.params[i].type.names.length === 1 ? element.params[i].type.names[0] : element.params[i].type.names) : '',
'description': element.params[i].description || '',
'default': element.params[i].defaultvalue || '',
'optional': typeof element.params[i].optional === 'boolean' ? element.params[i].optional : '',
'nullable': typeof element.params[i].nullable === 'boolean' ? element.params[i].nullable : ''
});
}
}
}
}
else if ((element.kind === 'member') || (element.kind == 'constant')) {
var parentClass = findClass(parentNode, element.memberof);
if (parentClass != null) {
parentClass.members.push({
'name': element.name,
'access': element.access || '',
'virtual': !!element.virtual,
'description': element.description || (((element.properties !== undefined) && (element.properties.length === 1)) ? (element.properties[0].description || '') : ''), //properties
'type': element.type ? (element.type.length === 1 ? element.type[0] : element.type) : '',
'default': element.defaultvalue || '',
});
}
}
});
}
/**
@param {TAFFY} data
@param {object} opts
*/
exports.publish = function (data, opts) {
var root = {};
data({undocumented: true}).remove();
graft2(root, data().get());
var fs = require('fs');
fs.writeFileSync(opts.destination, JSON.stringify(root), 'utf8');
};

230
tasks/buildtsdoc.js Normal file
View file

@ -0,0 +1,230 @@
/**
* Add comments in a TypeScript definition file
*/
'use strict';
var ts = require('typescript-services');
var fs = require('fs');
var TypeScriptDocGenerator = (function () {
function TypeScriptDocGenerator(tsDefFileName, jsdocJsonFileName) {
this.pos = 0;
this.nbCharsAdded = 0;
this.jsonDocsFileName = jsdocJsonFileName;
this.tsDefFileName = tsDefFileName;
this.jsonDocsFileContent = fs.readFileSync(this.jsonDocsFileName, 'utf-8');
this.docs = JSON.parse(this.jsonDocsFileContent);
this.tsDefFileContent = fs.readFileSync(this.tsDefFileName, 'utf-8');
this.tree = ts.Parser.parse('', ts.SimpleText.fromString(this.tsDefFileContent), true, new ts.ParseOptions(ts.LanguageVersion.EcmaScript5, true));
this.sourceUnit = this.tree.sourceUnit();
this.lineMap = this.tree.lineMap();
}
TypeScriptDocGenerator.prototype.completePrefix = function (oldPrefix, appendedPrefix) {
if (oldPrefix == "") {
return appendedPrefix;
}
else {
return oldPrefix + "." + appendedPrefix;
}
};
TypeScriptDocGenerator.prototype.repeatSpaces = function (nb) {
var res = "";
for (var i = 0; i < nb; i++) {
res += " ";
}
return res;
};
TypeScriptDocGenerator.prototype.leadingWidth = function (nodeOrToken) {
if (nodeOrToken != null) {
for (var i = 0; i < nodeOrToken.childCount() ; i++) {
var ltw = nodeOrToken.childAt(i).leadingTriviaWidth();
if (ltw > 0) {
return ltw;
}
}
}
return 0;
};
TypeScriptDocGenerator.prototype.insertComment = function (commentLines, position) {
if ((commentLines != null) && (commentLines.length > 0)) {
var nbChars = 0;
for (var i = 0; i < commentLines.length; i++) {
nbChars += commentLines[i].trim().length;
}
if (nbChars > 0) {
var lc = this.lineMap.getLineAndCharacterFromPosition(position);
var nbSpaces = lc.character();
var startLinePosition = this.lineMap.getLineStartPosition(lc.line());
var comment = "\n" + this.repeatSpaces(nbSpaces) + "/**\n";
for (var i = 0; i < commentLines.length; i++) {
comment += this.repeatSpaces(nbSpaces) + "* " + commentLines[i] + "\n";
}
comment += this.repeatSpaces(nbSpaces) + "*/\n";
this.tsDefFileContent = this.tsDefFileContent.substr(0, startLinePosition + this.nbCharsAdded) + comment + this.tsDefFileContent.substr(startLinePosition + this.nbCharsAdded);
this.nbCharsAdded += comment.length;
}
}
};
TypeScriptDocGenerator.prototype.findClass = function (className) {
if (className.indexOf("p2.") == 0) {
className = className.replace("p2.", "Phaser.Physics.P2.");
}
var elements = this.docs.classes.filter(function (element) {
return (element.name === className);
});
return elements[0];
};
TypeScriptDocGenerator.prototype.generateMemberComments = function (className, memberName) {
var c = this.findClass(className);
if (c != null) {
for (var i = 0; i < c.members.length; i++) {
if (c.members[i].name == memberName) {
var m = c.members[i];
var comments = new Array();
comments = comments.concat(m.description.split("\n"));
if ((m.default != null) && (m.default != "")) {
comments.push("Default: " + m.default);
}
return comments;
}
}
}
else {
return null;
}
};
TypeScriptDocGenerator.prototype.generateClassComments = function (className) {
var c = this.findClass(className);
if (c != null) {
var comments = new Array();
comments = comments.concat(c.description.split("\n"));
return comments;
}
else {
return null;
}
};
TypeScriptDocGenerator.prototype.generateConstructorComments = function (className) {
var c = this.findClass(className);
if (c != null) {
var con = c.constructor;
var comments = new Array();
comments = comments.concat(con.description.split("\n"));
if (con.parameters.length > 0) {
comments.push("");
}
for (var j = 0; j < con.parameters.length; j++) {
var p = con.parameters[j];
if (p.type === "*") {
p.name = "args";
}
var def = "";
if ((p.default != null) && (p.default != "")) {
def = " - Default: " + p.default;
}
comments.push("@param " + p.name + " - " + p.description + def);
}
return comments;
}
else {
return null;
}
};
TypeScriptDocGenerator.prototype.generateFunctionComments = function (className, functionName) {
var c = this.findClass(className);
if (c != null) {
for (var i = 0; i < c.functions.length; i++) {
if (c.functions[i].name == functionName) {
var f = c.functions[i];
var comments = new Array();
comments = comments.concat(f.description.split("\n"));
if (f.parameters.length > 0) {
comments.push("");
}
for (var j = 0; j < f.parameters.length; j++) {
var p = f.parameters[j];
if (p.type === "*") {
p.name = "args";
}
var def = "";
if ((p.default != null) && (p.default != "")) {
def = " - Default: " + p.default;
}
comments.push("@param " + p.name + " - " + p.description + def);
}
if (f.returns != null) {
comments.push("@return - " + f.returns.description);
}
return comments;
}
}
}
else {
return null;
}
};
TypeScriptDocGenerator.prototype.scanClass = function (c, fullName, classPos) {
for (var i = 0; i < c.childCount() ; i++) {
var elem = c.childAt(i);
if (elem != null) {
switch (elem.kind()) {
case ts.SyntaxKind.List:
classPos = this.scanClass(elem, fullName, classPos);
break;
case ts.SyntaxKind.ConstructorDeclaration:
this.insertComment(this.generateConstructorComments(fullName), classPos + this.leadingWidth(elem));
break;
case ts.SyntaxKind.MemberVariableDeclaration:
this.insertComment(this.generateMemberComments(fullName, elem.variableDeclarator.propertyName.fullText().trim()), classPos + this.leadingWidth(elem));
break;
case ts.SyntaxKind.MemberFunctionDeclaration:
this.insertComment(this.generateFunctionComments(fullName, elem.propertyName.fullText().trim()), classPos + this.leadingWidth(elem));
break;
}
if (elem.kind() != ts.SyntaxKind.List) {
classPos += elem.fullWidth();
}
}
}
return classPos;
};
TypeScriptDocGenerator.prototype.scan = function (elem, prefix) {
if (elem != null) {
switch (elem.kind()) {
case ts.SyntaxKind.List:
for (var k = 0; k < elem.childCount() ; k++) {
this.scan(elem.childAt(k), prefix);
}
break;
case ts.SyntaxKind.InterfaceDeclaration:
break;
case ts.SyntaxKind.ClassDeclaration:
var fullClassName = this.completePrefix(prefix, elem.identifier.fullText().trim());
this.insertComment(this.generateClassComments(fullClassName), this.pos + this.leadingWidth(elem));
this.scanClass(elem, fullClassName, this.pos);
break;
case ts.SyntaxKind.ModuleDeclaration:
for (var j = 0; j < elem.childCount() ; j++) {
this.scan(elem.childAt(j), this.completePrefix(prefix, elem.name.fullText().trim()));
}
break;
}
if ((elem.kind() != ts.SyntaxKind.List) && (elem.kind() != ts.SyntaxKind.ModuleDeclaration)) {
this.pos += elem.fullWidth();
}
}
};
TypeScriptDocGenerator.prototype.getTsDefCommentedFileContent = function () {
for (var i = 0; i < this.sourceUnit.childCount() ; i++) {
this.scan(this.sourceUnit.childAt(i), "");
}
return this.tsDefFileContent;
};
return TypeScriptDocGenerator;
})();
module.exports = function (grunt) {
grunt.registerMultiTask('buildtsdoc', 'Generate a TypeScript def with comments', function () {
var tsdg = new TypeScriptDocGenerator(this.data.tsDefFileName, this.data.jsdocJsonFileName);
fs.writeFileSync(this.data.dest, tsdg.getTsDefCommentedFileContent(), 'utf8');
});
};

26
tasks/exportdocjson.js Normal file
View file

@ -0,0 +1,26 @@
/**
* A quick stub-task for export the jsdoc in a JSON file
* This should probably be migrated to use grunt-jsdoc@beta (for jsdoc 3.x) or similar.
*/
'use strict';
module.exports = function (grunt) {
grunt.registerTask('exportdocjson', 'Export the project documentation in json format', function () {
var done = this.async();
grunt.util.spawn({
cmd: 'jsdoc',
args: ['-c', './tasks/jsdocexportjson-conf.json'],
}, function (error, result, code) {
if (error) {
//grunt.fail.warn("" + result);
done();
} else {
done();
}
});
});
};

View file

@ -0,0 +1,47 @@
{
"tags": {
"allowUnknownTags": true
},
"source": {
"include": [
"./docs/pixi-jsdoc.js",
"./src/Phaser.js",
"./src/animation/",
"./src/core/",
"./src/gameobjects/",
"./src/geom/",
"./src/input/",
"./src/loader/",
"./src/math/",
"./src/net/",
"./src/particles/",
"./src/physics/",
"./src/sound/",
"./src/system/",
"./src/tilemap/",
"./src/time/",
"./src/tween/",
"./src/utils/"
],
"includePattern": ".+\\.js(doc)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": [
"./tasks/jsdoc-plugins/namealias",
"./tasks/jsdoc-plugins/filterpixi",
"./tasks/jsdoc-plugins/proptomember",
"./tasks/jsdoc-plugins/shortlinks"
],
"markdown" : {
"parser" : "gfm",
"hardwrap" : true
},
"opts": {
"encoding": "utf8",
"recurse": true,
"private": true,
"lenient": true,
"destination": "./out/docs.json",
"template": "./resources/jsdoc Export JSON/template"
}
}

View file

@ -0,0 +1,13 @@
module.exports = {
pixi: {
tsDefFileName: 'typescript/pixi.d.ts',
jsdocJsonFileName: 'out/docs.json',
dest: 'typescript/pixi.comments.d.ts'
},
phaser: {
tsDefFileName: 'typescript/phaser.d.ts',
jsdocJsonFileName: 'out/docs.json',
dest: 'typescript/phaser.comments.d.ts'
},
};

View file

@ -111,6 +111,14 @@ module.exports = {
to: '<img src="http://phaser.io/images/github/shot14.jpg">'
}
]
}
},
phasertsdefheader: {
src: ['typescript/phaser.comments.d.ts'],
dest: 'typescript/phaser.comments.d.ts',
replacements: [{
from: 'path="pixi.d.ts"',
to: 'path="pixi.comments.d.ts"'
}]
}
};

25375
typescript/phaser.comments.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

5117
typescript/pixi.comments.d.ts vendored Normal file

File diff suppressed because it is too large Load diff