Skip to content

Commit df6b946

Browse files
committed
Add grunt task to generate d.ts with comments
Add "tsdocs" grunt task to generate d.ts files with comments.
1 parent 6d967b6 commit df6b946

10 files changed

Lines changed: 30933 additions & 2 deletions

File tree

Gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ module.exports = function (grunt) {
2222

2323
grunt.registerTask('docs', [ 'clean:docs', 'pixidoc', 'builddoc', 'replace:docs', 'clean:out']);
2424

25+
grunt.registerTask('tsdocs', ['pixidoc', 'exportdocjson', 'buildtsdoc:pixi', 'buildtsdoc:phaser', 'replace:phasertsdefheader', 'clean:out']);
2526
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"load-grunt-config": "~0.7.2",
4444
"yuidocjs": "^0.3.50",
4545
"jsdoc": "~3.3.0-alpha10",
46-
"grunt-jsdoc": "~0.5.7"
46+
"grunt-jsdoc": "~0.5.7",
47+
"typescript-services": "~0.1.5"
4748
}
4849
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
@overview Export the jsdoc comments in a json file
3+
@version 1.0
4+
*/
5+
'use strict';
6+
7+
function findClass(parentNode, className)
8+
{
9+
var elements =
10+
parentNode.classes
11+
.filter(function (element) {
12+
return (element.name === className);
13+
});
14+
15+
return elements[0];
16+
}
17+
18+
function graft2(parentNode, childNodes) {
19+
if (!parentNode.classes) {
20+
parentNode.classes = [];
21+
}
22+
23+
childNodes.forEach(function (element, index) {
24+
if (element.kind === 'class') {
25+
var thisClass = {
26+
'name': element.longname,
27+
'description': element.classdesc || element.description || '',
28+
'constructor': {
29+
'name': element.name,
30+
'description': element.description || '',
31+
'parameters': []
32+
},
33+
'functions': [],
34+
'members': []
35+
};
36+
37+
if (element.params) {
38+
for (i = 0, len = element.params.length; i < len; i++) {
39+
thisClass.constructor.parameters.push({
40+
'name': element.params[i].name,
41+
'type': element.params[i].type ? (element.params[i].type.names.length === 1 ? element.params[i].type.names[0] : element.params[i].type.names) : '',
42+
'description': element.params[i].description || '',
43+
'default': element.params[i].defaultvalue || '',
44+
'optional': typeof element.params[i].optional === 'boolean' ? element.params[i].optional : '',
45+
'nullable': typeof element.params[i].nullable === 'boolean' ? element.params[i].nullable : ''
46+
});
47+
}
48+
}
49+
50+
parentNode.classes.push(thisClass);
51+
}
52+
else if (element.kind === 'function')
53+
{
54+
var parentClass = findClass(parentNode, element.memberof);
55+
56+
var thisFunction = {
57+
'name': element.name,
58+
'description': element.description || '',
59+
'parameters': []
60+
};
61+
62+
if (parentClass != null) {
63+
64+
parentClass.functions.push(thisFunction);
65+
66+
if (element.returns) {
67+
thisFunction.returns = {
68+
'type': element.returns[0].type ? (element.returns[0].type.names.length === 1 ? element.returns[0].type.names[0] : element.returns[0].type.names) : '',
69+
'description': element.returns[0].description || ''
70+
};
71+
}
72+
73+
if (element.params) {
74+
for (i = 0, len = element.params.length; i < len; i++) {
75+
thisFunction.parameters.push({
76+
'name': element.params[i].name,
77+
'type': element.params[i].type ? (element.params[i].type.names.length === 1 ? element.params[i].type.names[0] : element.params[i].type.names) : '',
78+
'description': element.params[i].description || '',
79+
'default': element.params[i].defaultvalue || '',
80+
'optional': typeof element.params[i].optional === 'boolean' ? element.params[i].optional : '',
81+
'nullable': typeof element.params[i].nullable === 'boolean' ? element.params[i].nullable : ''
82+
});
83+
}
84+
}
85+
}
86+
}
87+
else if ((element.kind === 'member') || (element.kind == 'constant')) {
88+
var parentClass = findClass(parentNode, element.memberof);
89+
if (parentClass != null) {
90+
parentClass.members.push({
91+
'name': element.name,
92+
'access': element.access || '',
93+
'virtual': !!element.virtual,
94+
'description': element.description || (((element.properties !== undefined) && (element.properties.length === 1)) ? (element.properties[0].description || '') : ''), //properties
95+
'type': element.type ? (element.type.length === 1 ? element.type[0] : element.type) : '',
96+
'default': element.defaultvalue || '',
97+
});
98+
}
99+
}
100+
});
101+
}
102+
/**
103+
@param {TAFFY} data
104+
@param {object} opts
105+
*/
106+
exports.publish = function (data, opts) {
107+
var root = {};
108+
data({undocumented: true}).remove();
109+
110+
graft2(root, data().get());
111+
var fs = require('fs');
112+
fs.writeFileSync(opts.destination, JSON.stringify(root), 'utf8');
113+
};

tasks/buildtsdoc.js

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/**
2+
* Add comments in a TypeScript definition file
3+
*/
4+
'use strict';
5+
var ts = require('typescript-services');
6+
var fs = require('fs');
7+
8+
var TypeScriptDocGenerator = (function () {
9+
function TypeScriptDocGenerator(tsDefFileName, jsdocJsonFileName) {
10+
this.pos = 0;
11+
this.nbCharsAdded = 0;
12+
this.jsonDocsFileName = jsdocJsonFileName;
13+
this.tsDefFileName = tsDefFileName;
14+
this.jsonDocsFileContent = fs.readFileSync(this.jsonDocsFileName, 'utf-8');
15+
this.docs = JSON.parse(this.jsonDocsFileContent);
16+
this.tsDefFileContent = fs.readFileSync(this.tsDefFileName, 'utf-8');
17+
this.tree = ts.Parser.parse('', ts.SimpleText.fromString(this.tsDefFileContent), true, new ts.ParseOptions(ts.LanguageVersion.EcmaScript5, true));
18+
this.sourceUnit = this.tree.sourceUnit();
19+
this.lineMap = this.tree.lineMap();
20+
}
21+
TypeScriptDocGenerator.prototype.completePrefix = function (oldPrefix, appendedPrefix) {
22+
if (oldPrefix == "") {
23+
return appendedPrefix;
24+
}
25+
else {
26+
return oldPrefix + "." + appendedPrefix;
27+
}
28+
};
29+
TypeScriptDocGenerator.prototype.repeatSpaces = function (nb) {
30+
var res = "";
31+
for (var i = 0; i < nb; i++) {
32+
res += " ";
33+
}
34+
return res;
35+
};
36+
TypeScriptDocGenerator.prototype.leadingWidth = function (nodeOrToken) {
37+
if (nodeOrToken != null) {
38+
for (var i = 0; i < nodeOrToken.childCount() ; i++) {
39+
var ltw = nodeOrToken.childAt(i).leadingTriviaWidth();
40+
if (ltw > 0) {
41+
return ltw;
42+
}
43+
}
44+
}
45+
return 0;
46+
};
47+
TypeScriptDocGenerator.prototype.insertComment = function (commentLines, position) {
48+
if ((commentLines != null) && (commentLines.length > 0)) {
49+
var nbChars = 0;
50+
for (var i = 0; i < commentLines.length; i++) {
51+
nbChars += commentLines[i].trim().length;
52+
}
53+
if (nbChars > 0) {
54+
var lc = this.lineMap.getLineAndCharacterFromPosition(position);
55+
var nbSpaces = lc.character();
56+
var startLinePosition = this.lineMap.getLineStartPosition(lc.line());
57+
var comment = "\n" + this.repeatSpaces(nbSpaces) + "/**\n";
58+
for (var i = 0; i < commentLines.length; i++) {
59+
comment += this.repeatSpaces(nbSpaces) + "* " + commentLines[i] + "\n";
60+
}
61+
comment += this.repeatSpaces(nbSpaces) + "*/\n";
62+
this.tsDefFileContent = this.tsDefFileContent.substr(0, startLinePosition + this.nbCharsAdded) + comment + this.tsDefFileContent.substr(startLinePosition + this.nbCharsAdded);
63+
this.nbCharsAdded += comment.length;
64+
}
65+
}
66+
};
67+
TypeScriptDocGenerator.prototype.findClass = function (className) {
68+
if (className.indexOf("p2.") == 0) {
69+
className = className.replace("p2.", "Phaser.Physics.P2.");
70+
}
71+
var elements = this.docs.classes.filter(function (element) {
72+
return (element.name === className);
73+
});
74+
return elements[0];
75+
};
76+
TypeScriptDocGenerator.prototype.generateMemberComments = function (className, memberName) {
77+
var c = this.findClass(className);
78+
if (c != null) {
79+
for (var i = 0; i < c.members.length; i++) {
80+
if (c.members[i].name == memberName) {
81+
var m = c.members[i];
82+
var comments = new Array();
83+
comments = comments.concat(m.description.split("\n"));
84+
if ((m.default != null) && (m.default != "")) {
85+
comments.push("Default: " + m.default);
86+
}
87+
return comments;
88+
}
89+
}
90+
}
91+
else {
92+
return null;
93+
}
94+
};
95+
TypeScriptDocGenerator.prototype.generateClassComments = function (className) {
96+
var c = this.findClass(className);
97+
if (c != null) {
98+
var comments = new Array();
99+
comments = comments.concat(c.description.split("\n"));
100+
return comments;
101+
}
102+
else {
103+
return null;
104+
}
105+
};
106+
TypeScriptDocGenerator.prototype.generateConstructorComments = function (className) {
107+
var c = this.findClass(className);
108+
if (c != null) {
109+
var con = c.constructor;
110+
var comments = new Array();
111+
comments = comments.concat(con.description.split("\n"));
112+
if (con.parameters.length > 0) {
113+
comments.push("");
114+
}
115+
for (var j = 0; j < con.parameters.length; j++) {
116+
var p = con.parameters[j];
117+
if (p.type === "*") {
118+
p.name = "args";
119+
}
120+
var def = "";
121+
if ((p.default != null) && (p.default != "")) {
122+
def = " - Default: " + p.default;
123+
}
124+
comments.push("@param " + p.name + " - " + p.description + def);
125+
}
126+
return comments;
127+
}
128+
else {
129+
return null;
130+
}
131+
};
132+
TypeScriptDocGenerator.prototype.generateFunctionComments = function (className, functionName) {
133+
var c = this.findClass(className);
134+
if (c != null) {
135+
for (var i = 0; i < c.functions.length; i++) {
136+
if (c.functions[i].name == functionName) {
137+
var f = c.functions[i];
138+
var comments = new Array();
139+
comments = comments.concat(f.description.split("\n"));
140+
if (f.parameters.length > 0) {
141+
comments.push("");
142+
}
143+
for (var j = 0; j < f.parameters.length; j++) {
144+
var p = f.parameters[j];
145+
if (p.type === "*") {
146+
p.name = "args";
147+
}
148+
var def = "";
149+
if ((p.default != null) && (p.default != "")) {
150+
def = " - Default: " + p.default;
151+
}
152+
comments.push("@param " + p.name + " - " + p.description + def);
153+
}
154+
if (f.returns != null) {
155+
comments.push("@return - " + f.returns.description);
156+
}
157+
return comments;
158+
}
159+
}
160+
}
161+
else {
162+
return null;
163+
}
164+
};
165+
TypeScriptDocGenerator.prototype.scanClass = function (c, fullName, classPos) {
166+
for (var i = 0; i < c.childCount() ; i++) {
167+
var elem = c.childAt(i);
168+
if (elem != null) {
169+
switch (elem.kind()) {
170+
case ts.SyntaxKind.List:
171+
classPos = this.scanClass(elem, fullName, classPos);
172+
break;
173+
case ts.SyntaxKind.ConstructorDeclaration:
174+
this.insertComment(this.generateConstructorComments(fullName), classPos + this.leadingWidth(elem));
175+
break;
176+
case ts.SyntaxKind.MemberVariableDeclaration:
177+
this.insertComment(this.generateMemberComments(fullName, elem.variableDeclarator.propertyName.fullText().trim()), classPos + this.leadingWidth(elem));
178+
break;
179+
case ts.SyntaxKind.MemberFunctionDeclaration:
180+
this.insertComment(this.generateFunctionComments(fullName, elem.propertyName.fullText().trim()), classPos + this.leadingWidth(elem));
181+
break;
182+
}
183+
if (elem.kind() != ts.SyntaxKind.List) {
184+
classPos += elem.fullWidth();
185+
}
186+
}
187+
}
188+
return classPos;
189+
};
190+
TypeScriptDocGenerator.prototype.scan = function (elem, prefix) {
191+
if (elem != null) {
192+
switch (elem.kind()) {
193+
case ts.SyntaxKind.List:
194+
for (var k = 0; k < elem.childCount() ; k++) {
195+
this.scan(elem.childAt(k), prefix);
196+
}
197+
break;
198+
case ts.SyntaxKind.InterfaceDeclaration:
199+
break;
200+
case ts.SyntaxKind.ClassDeclaration:
201+
var fullClassName = this.completePrefix(prefix, elem.identifier.fullText().trim());
202+
this.insertComment(this.generateClassComments(fullClassName), this.pos + this.leadingWidth(elem));
203+
this.scanClass(elem, fullClassName, this.pos);
204+
break;
205+
case ts.SyntaxKind.ModuleDeclaration:
206+
for (var j = 0; j < elem.childCount() ; j++) {
207+
this.scan(elem.childAt(j), this.completePrefix(prefix, elem.name.fullText().trim()));
208+
}
209+
break;
210+
}
211+
if ((elem.kind() != ts.SyntaxKind.List) && (elem.kind() != ts.SyntaxKind.ModuleDeclaration)) {
212+
this.pos += elem.fullWidth();
213+
}
214+
}
215+
};
216+
TypeScriptDocGenerator.prototype.getTsDefCommentedFileContent = function () {
217+
for (var i = 0; i < this.sourceUnit.childCount() ; i++) {
218+
this.scan(this.sourceUnit.childAt(i), "");
219+
}
220+
return this.tsDefFileContent;
221+
};
222+
return TypeScriptDocGenerator;
223+
})();
224+
225+
module.exports = function (grunt) {
226+
grunt.registerMultiTask('buildtsdoc', 'Generate a TypeScript def with comments', function () {
227+
var tsdg = new TypeScriptDocGenerator(this.data.tsDefFileName, this.data.jsdocJsonFileName);
228+
fs.writeFileSync(this.data.dest, tsdg.getTsDefCommentedFileContent(), 'utf8');
229+
});
230+
};

tasks/exportdocjson.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* A quick stub-task for export the jsdoc in a JSON file
3+
* This should probably be migrated to use grunt-jsdoc@beta (for jsdoc 3.x) or similar.
4+
*/
5+
'use strict';
6+
7+
module.exports = function (grunt) {
8+
9+
grunt.registerTask('exportdocjson', 'Export the project documentation in json format', function () {
10+
11+
var done = this.async();
12+
13+
grunt.util.spawn({
14+
cmd: 'jsdoc',
15+
args: ['-c', './tasks/jsdocexportjson-conf.json'],
16+
}, function (error, result, code) {
17+
if (error) {
18+
//grunt.fail.warn("" + result);
19+
done();
20+
} else {
21+
done();
22+
}
23+
});
24+
25+
});
26+
};

0 commit comments

Comments
 (0)