Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform/web/js/jsdoc2rst/publish.js
10279 views
1
/* eslint-disable strict */
2
3
'use strict';
4
5
const fs = require('fs');
6
7
class JSDoclet {
8
constructor(doc) {
9
this.doc = doc;
10
this.description = doc['description'] || '';
11
this.name = doc['name'] || 'unknown';
12
this.longname = doc['longname'] || '';
13
this.types = [];
14
if (doc['type'] && doc['type']['names']) {
15
this.types = doc['type']['names'].slice();
16
}
17
this.type = this.types.length > 0 ? this.types.join('\\|') : '*';
18
this.variable = doc['variable'] || false;
19
this.kind = doc['kind'] || '';
20
this.memberof = doc['memberof'] || null;
21
this.scope = doc['scope'] || '';
22
this.members = [];
23
this.optional = doc['optional'] || false;
24
this.defaultvalue = doc['defaultvalue'];
25
this.summary = doc['summary'] || null;
26
this.classdesc = doc['classdesc'] || null;
27
28
// Parameters (functions)
29
this.params = [];
30
this.returns = doc['returns'] ? doc['returns'][0]['type']['names'][0] : 'void';
31
this.returns_desc = doc['returns'] ? doc['returns'][0]['description'] : null;
32
33
this.params = (doc['params'] || []).slice().map((p) => new JSDoclet(p));
34
35
// Custom tags
36
this.tags = doc['tags'] || [];
37
this.header = this.tags.filter((t) => t['title'] === 'header').map((t) => t['text']).pop() || null;
38
}
39
40
add_member(obj) {
41
this.members.push(obj);
42
}
43
44
is_static() {
45
return this.scope === 'static';
46
}
47
48
is_instance() {
49
return this.scope === 'instance';
50
}
51
52
is_object() {
53
return this.kind === 'Object' || (this.kind === 'typedef' && this.type === 'Object');
54
}
55
56
is_class() {
57
return this.kind === 'class';
58
}
59
60
is_function() {
61
return this.kind === 'function' || (this.kind === 'typedef' && this.type === 'function');
62
}
63
64
is_module() {
65
return this.kind === 'module';
66
}
67
}
68
69
function format_table(f, data, depth = 0) {
70
if (!data.length) {
71
return;
72
}
73
74
const column_sizes = new Array(data[0].length).fill(0);
75
76
data.forEach((row) => {
77
row.forEach((e, idx) => {
78
column_sizes[idx] = Math.max(e.length, column_sizes[idx]);
79
});
80
});
81
82
const indent = ' '.repeat(depth);
83
let sep = indent;
84
column_sizes.forEach((size) => {
85
sep += '+';
86
sep += '-'.repeat(size + 2);
87
});
88
sep += '+\n';
89
f.write(sep);
90
91
data.forEach((row) => {
92
let row_text = `${indent}|`;
93
row.forEach((entry, idx) => {
94
row_text += ` ${entry.padEnd(column_sizes[idx])} |`;
95
});
96
row_text += '\n';
97
f.write(row_text);
98
f.write(sep);
99
});
100
101
f.write('\n');
102
}
103
104
function make_header(header, sep) {
105
return `${header}\n${sep.repeat(header.length)}\n\n`;
106
}
107
108
function indent_multiline(text, depth) {
109
const indent = ' '.repeat(depth);
110
return text.split('\n').map((l) => (l === '' ? l : indent + l)).join('\n');
111
}
112
113
function make_rst_signature(obj, types = false, style = false) {
114
let out = '';
115
const fmt = style ? '*' : '';
116
obj.params.forEach((arg, idx) => {
117
if (idx > 0) {
118
if (arg.optional) {
119
out += ` ${fmt}[`;
120
}
121
out += ', ';
122
} else {
123
out += ' ';
124
if (arg.optional) {
125
out += `${fmt}[ `;
126
}
127
}
128
if (types) {
129
out += `${arg.type} `;
130
}
131
const variable = arg.variable ? '...' : '';
132
const defval = arg.defaultvalue !== undefined ? `=${arg.defaultvalue}` : '';
133
out += `${variable}${arg.name}${defval}`;
134
if (arg.optional) {
135
out += ` ]${fmt}`;
136
}
137
});
138
out += ' ';
139
return out;
140
}
141
142
function make_rst_param(f, obj, depth = 0) {
143
const indent = ' '.repeat(depth * 3);
144
f.write(indent);
145
f.write(`:param ${obj.type} ${obj.name}:\n`);
146
f.write(indent_multiline(obj.description, (depth + 1) * 3));
147
f.write('\n\n');
148
}
149
150
function make_rst_attribute(f, obj, depth = 0, brief = false) {
151
const indent = ' '.repeat(depth * 3);
152
f.write(indent);
153
f.write(`.. js:attribute:: ${obj.name}\n\n`);
154
155
if (brief) {
156
if (obj.summary) {
157
f.write(indent_multiline(obj.summary, (depth + 1) * 3));
158
}
159
f.write('\n\n');
160
return;
161
}
162
163
f.write(indent_multiline(obj.description, (depth + 1) * 3));
164
f.write('\n\n');
165
166
f.write(indent);
167
f.write(` :type: ${obj.type}\n\n`);
168
169
if (obj.defaultvalue !== undefined) {
170
let defval = obj.defaultvalue;
171
if (defval === '') {
172
defval = '""';
173
}
174
f.write(indent);
175
f.write(` :value: \`\`${defval}\`\`\n\n`);
176
}
177
}
178
179
function make_rst_function(f, obj, depth = 0) {
180
let prefix = '';
181
if (obj.is_instance()) {
182
prefix = 'prototype.';
183
}
184
185
const indent = ' '.repeat(depth * 3);
186
const sig = make_rst_signature(obj);
187
f.write(indent);
188
f.write(`.. js:function:: ${prefix}${obj.name}(${sig})\n`);
189
f.write('\n');
190
191
f.write(indent_multiline(obj.description, (depth + 1) * 3));
192
f.write('\n\n');
193
194
obj.params.forEach((param) => {
195
make_rst_param(f, param, depth + 1);
196
});
197
198
if (obj.returns !== 'void') {
199
f.write(indent);
200
f.write(' :return:\n');
201
f.write(indent_multiline(obj.returns_desc, (depth + 2) * 3));
202
f.write('\n\n');
203
f.write(indent);
204
f.write(` :rtype: ${obj.returns}\n\n`);
205
}
206
}
207
208
function make_rst_object(f, obj) {
209
let brief = false;
210
// Our custom header flag.
211
if (obj.header !== null) {
212
f.write(make_header(obj.header, '-'));
213
f.write(`${obj.description}\n\n`);
214
brief = true;
215
}
216
217
// Format members table and descriptions
218
const data = [['type', 'name']].concat(obj.members.map((m) => [m.type, `:js:attr:\`${m.name}\``]));
219
220
f.write(make_header('Properties', '^'));
221
format_table(f, data, 0);
222
223
make_rst_attribute(f, obj, 0, brief);
224
225
if (!obj.members.length) {
226
return;
227
}
228
229
f.write(' **Property Descriptions**\n\n');
230
231
// Properties first
232
obj.members.filter((m) => !m.is_function()).forEach((m) => {
233
make_rst_attribute(f, m, 1);
234
});
235
236
// Callbacks last
237
obj.members.filter((m) => m.is_function()).forEach((m) => {
238
make_rst_function(f, m, 1);
239
});
240
}
241
242
function make_rst_class(f, obj) {
243
const header = obj.header ? obj.header : obj.name;
244
f.write(make_header(header, '-'));
245
246
if (obj.classdesc) {
247
f.write(`${obj.classdesc}\n\n`);
248
}
249
250
const funcs = obj.members.filter((m) => m.is_function());
251
function make_data(m) {
252
const base = m.is_static() ? obj.name : `${obj.name}.prototype`;
253
const params = make_rst_signature(m, true, true);
254
const sig = `:js:attr:\`${m.name} <${base}.${m.name}>\` **(**${params}**)**`;
255
return [m.returns, sig];
256
}
257
const sfuncs = funcs.filter((m) => m.is_static());
258
const ifuncs = funcs.filter((m) => !m.is_static());
259
260
f.write(make_header('Static Methods', '^'));
261
format_table(f, sfuncs.map((m) => make_data(m)));
262
263
f.write(make_header('Instance Methods', '^'));
264
format_table(f, ifuncs.map((m) => make_data(m)));
265
266
const sig = make_rst_signature(obj);
267
f.write(`.. js:class:: ${obj.name}(${sig})\n\n`);
268
f.write(indent_multiline(obj.description, 3));
269
f.write('\n\n');
270
271
obj.params.forEach((p) => {
272
make_rst_param(f, p, 1);
273
});
274
275
f.write(' **Static Methods**\n\n');
276
sfuncs.forEach((m) => {
277
make_rst_function(f, m, 1);
278
});
279
280
f.write(' **Instance Methods**\n\n');
281
ifuncs.forEach((m) => {
282
make_rst_function(f, m, 1);
283
});
284
}
285
286
function make_rst_module(f, obj) {
287
const header = obj.header !== null ? obj.header : obj.name;
288
f.write(make_header(header, '='));
289
f.write(obj.description);
290
f.write('\n\n');
291
}
292
293
function write_base_object(f, obj) {
294
if (obj.is_object()) {
295
make_rst_object(f, obj);
296
} else if (obj.is_function()) {
297
make_rst_function(f, obj);
298
} else if (obj.is_class()) {
299
make_rst_class(f, obj);
300
} else if (obj.is_module()) {
301
make_rst_module(f, obj);
302
}
303
}
304
305
function generate(f, docs) {
306
const globs = [];
307
const SYMBOLS = {};
308
docs.filter((d) => !d.ignore && d.kind !== 'package').forEach((d) => {
309
SYMBOLS[d.name] = d;
310
if (d.memberof) {
311
const up = SYMBOLS[d.memberof];
312
if (up === undefined) {
313
console.log(d); // eslint-disable-line no-console
314
console.log(`Undefined symbol! ${d.memberof}`); // eslint-disable-line no-console
315
throw new Error('Undefined symbol!');
316
}
317
SYMBOLS[d.memberof].add_member(d);
318
} else {
319
globs.push(d);
320
}
321
});
322
323
f.write('.. _doc_html5_shell_classref:\n\n');
324
globs.forEach((obj) => write_base_object(f, obj));
325
}
326
327
/**
328
* Generate documentation output.
329
*
330
* @param {TAFFY} data - A TaffyDB collection representing
331
* all the symbols documented in your code.
332
* @param {object} opts - An object with options information.
333
*/
334
exports.publish = function (data, opts) {
335
const docs = data().get().filter((doc) => !doc.undocumented && !doc.ignore).map((doc) => new JSDoclet(doc));
336
const dest = opts.destination;
337
if (dest === 'dry-run') {
338
process.stdout.write('Dry run... ');
339
generate({
340
write: function () { /* noop */ },
341
}, docs);
342
process.stdout.write('Okay!\n');
343
return;
344
}
345
if (dest !== '' && !dest.endsWith('.rst')) {
346
throw new Error('Destination file must be either a ".rst" file, or an empty string (for printing to stdout)');
347
}
348
if (dest !== '') {
349
const f = fs.createWriteStream(dest);
350
generate(f, docs);
351
} else {
352
generate(process.stdout, docs);
353
}
354
};
355
356