Skip to content

Commit 6e139bc

Browse files
committed
Merge pull request phaserjs#1552 from vulvulune/dev
Add grunt task to generate d.ts with comments
2 parents 6d967b6 + 0c8b5f4 commit 6e139bc

11 files changed

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

0 commit comments

Comments
 (0)