UNPKG

9.01 kBJavaScriptView Raw
1(function () {
2 /*global env*/
3 var dfm = require('./dfm');
4 var manager = require('./itemManager');
5 var itemHandler = require('./itemHandler');
6 var config = null;
7 var base = '_yamlGeneratorOutput';
8 var globalUid = 'global';
9 var uidPrefix = '';
10 var yamlMime = '### YamlMime:UniversalReference';
11 var outputFileExt = '.yml';
12 var jsdocConfigPath = '_jsdocConfTemp.json';
13 var packageName = '';
14
15 function setSourceInfo(item, doclet) {
16 if (config.repo) {
17 var path = doclet.meta.path.replace(env.pwd + '\\', '') + '\\' + doclet.meta.filename;
18 if (path.split('\\').length > 2) {
19 path = path.split('\\').splice(2).join('\\');
20 }
21 item.source = {
22 id: item.id,
23 path: path,
24 startLine: doclet.meta.lineno,
25 remote: {
26 branch: config.repo.branch,
27 path: path,
28 repo: config.repo.url
29 }
30 };
31 }
32 }
33
34 function getClasses(classes, fileMap) {
35 manager.items.forEach(function (item) {
36 switch (item.type) {
37 case 'Class':
38 classes[item.uid] = {
39 items: [item],
40 referenceMap: {}
41 };
42 fileMap[item.uid] = item.uid;
43 break;
44 case 'Constructor':
45 case 'Function':
46 case 'Member':
47 var parentId = item.parent || globalUid;
48 var parent = classes[parentId];
49 if (parent === undefined) {
50 console.log(parentId + ' is not a class, ignored.');
51 break;
52 }
53 parent.items.push(item);
54 if (parentId === globalUid) {
55 (parent.items[0].children = parent.items[0].children || []).push(item.uid);
56 }
57 fileMap[item.uid] = parentId;
58 (item.syntax.parameters || []).forEach(function (p) {
59 (p.type || []).forEach(function (t) {
60 classes[parentId].referenceMap[t] = true;
61 });
62 });
63 if (item.syntax.return) {
64 (item.syntax.return.type || []).forEach(function (t) {
65 classes[parentId].referenceMap[t] = true;
66 });
67 }
68 break;
69 }
70 });
71 return classes;
72 }
73
74 function serializeIndex(classes) {
75 var serializer = require('js-yaml');
76 var fs = require('fs');
77 if (!fs.existsSync(base)) {
78 fs.mkdirSync(base);
79 }
80
81 var index = {
82 items: [{
83 uid: packageName,
84 name: packageName,
85 langs: ['js'],
86 type: 'package',
87 summary: '',
88 children: []
89 }],
90 references: []
91 };
92
93 if (Object.keys(classes).length <= 1) {
94 console.log('Nothing extracted for ' + packageName);
95 return;
96 }
97
98 for (var id in classes) {
99 var classItem = classes[id];
100 if (id === globalUid) {
101 for (var i = 1; i < classItem.items.length; i++) {
102 index.items[0].children.push(classItem.items[i].uid);
103 index.items.push(classItem.items[i]);
104 }
105 } else {
106 if (classItem.items && classItem.items.length) {
107 index.items[0].children.push(classItem.items[0].uid);
108 index.references.push({
109 uid : classItem.items[0].uid,
110 name : classItem.items[0].name
111 });
112 }
113 }
114 }
115
116 index = JSON.parse(JSON.stringify(index));
117 fs.writeFileSync(base + '/index.yml', yamlMime + '\n' + serializer.safeDump(index));
118 console.log('index generated.');
119 }
120
121 function serializeToc(classes, fileMap) {
122 var serializer = require('js-yaml');
123 var fs = require('fs');
124
125 if (!fs.existsSync(base)) {
126 fs.mkdirSync(base);
127 }
128
129 var toc = [];
130 for (var id in classes) {
131 var classItem = classes[id];
132 // build references
133 classItem.references = [];
134 for (var r in classItem.referenceMap) {
135 var f = fileMap[r];
136 if (f !== undefined && f !== id) {
137 classItem.references.push({
138 uid: r,
139 name: r.indexOf('.') == -1 ? r : r.substring(r.indexOf('.') + 1),
140 fullName: r,
141 isExternal: f === undefined
142 });
143 }
144 }
145 classItem.referenceMap = undefined;
146 if (classItem.references.length == 0) {
147 classItem.references = undefined;
148 }
149
150 // something wrong in js-yaml, workaround it by serialize and deserialize from JSON
151 classItem = JSON.parse(JSON.stringify(classItem));
152 // replace \r, \n, space with dash
153 // filter global
154 if (id == globalUid) {
155 continue;
156 }
157
158 var fileName = id.replace(/[ \n\r]/g, '-') + outputFileExt;
159 if (fileName && fileName.split('.').length > 2) {
160 fileName = fileName.split('.').splice(1).join('.');
161 }
162 fs.writeFileSync(base + '/' + fileName, yamlMime + '\n' + serializer.safeDump(classItem));
163 console.log(fileName + ' generated.');
164
165 var tocItem = {
166 uid: id,
167 name: classItem.items[0].name
168 };
169
170 toc.push(tocItem);
171 }
172 toc.sort(function (a, b) {
173 var nameA = a.name.toUpperCase();
174 var nameB = b.name.toUpperCase();
175 if (nameA < nameB) {
176 return -1;
177 }
178 if (nameA > nameB) {
179 return 1;
180 }
181
182 return 0;
183 });
184
185 fs.writeFileSync(base + '/toc.yml', serializer.safeDump(toc));
186 console.log('toc.yml generated.');
187 }
188
189 var typeMap = {
190 'member': itemHandler.handleMember,
191 'function': itemHandler.handleFunction,
192 'class': itemHandler.handleClass
193 };
194
195 exports.handlers = {
196 newDoclet: function (e) {
197 var doclet = e.doclet;
198 // ignore anything whose parent is not a doclet
199 // except it's a class made with help function
200 if (doclet.memberof !== undefined && manager.itemsMap[uidPrefix + doclet.memberof] === undefined && doclet.kind !== 'class') {
201 return;
202 }
203 // ignore unrecognized kind
204 if (typeMap[doclet.kind] === undefined) {
205 console.log('unrecognized kind: ' + doclet.kind);
206 return;
207 }
208 // ignore unexported global member
209 if (doclet.memberof === undefined && doclet.kind != 'class' && !(doclet.meta && doclet.meta.code && typeof(doclet.meta.code.name) === 'string' && doclet.meta.code.name.indexOf('exports') == 0)) {
210 return;
211 }
212 // ignore inner function or member
213 if (doclet.kind === 'member' && doclet.scope === 'inner') {
214 return;
215 }
216
217 if (doclet.access === 'private') {
218 return;
219 }
220
221 if (doclet.name && doclet.name[0] === '_') {
222 return;
223 }
224
225 // ignore doclet without doucment
226 if (doclet.undocumented === true) {
227 return;
228 }
229
230 // ignore empty longname
231 if (!doclet.longname) {
232 return;
233 }
234 // basic properties
235 var item = {
236 uid: uidPrefix + doclet.longname,
237 id: uidPrefix + doclet.longname,
238 parent: (doclet.memberof && doclet.kind !== 'class') ? (uidPrefix + doclet.memberof) : undefined,
239 name: doclet.name,
240 summary: doclet.description ? dfm.convertLinkToGfm(doclet.description, uidPrefix) : dfm.convertLinkToGfm(doclet.summary, uidPrefix)
241 };
242 // set parent
243 if (item.parent !== undefined) {
244 var parent = manager.itemsMap[item.parent];
245 (parent.children = parent.children || []).push(item.uid);
246 }
247 // set full name
248 item.fullName = (item.parent ? item.parent + '.' : uidPrefix) + item.name;
249
250 // set source info
251 if (doclet.kind === 'class') {
252 setSourceInfo(item, doclet);
253 }
254
255 // pass custom tags
256 if ('tags' in doclet) {
257 item.tags = doclet.tags;
258 }
259
260 typeMap[doclet.kind](item, doclet, uidPrefix, manager);
261 manager.addItem(item);
262 },
263 parseBegin: function () {
264 var fse = require('fs-extra');
265 config = fse.readJsonSync(jsdocConfigPath);
266
267 if (config.repo && config.repo.url && !config.repo.url.endsWith('.git')) {
268 config.repo.url = config.repo.url + '.git';
269 }
270
271 // parse package.json to use package name
272 if (config.package) {
273 var packageJson = fse.readJsonSync(config.package);
274 if (packageJson && packageJson.name) {
275 packageName = packageJson.name;
276 globalUid = packageJson.name + '.' + globalUid;
277 uidPrefix = packageJson.name + '.';
278 }
279 }
280 manager.items.push(
281 {
282 uid: globalUid,
283 id: globalUid,
284 name: 'GLOBAL',
285 fullName: 'GLOBAL',
286 type: 'Class',
287 langs: ['js']
288 }
289 );
290 },
291 parseComplete: function () {
292 var classes = {};
293 var fileMap = {};
294 getClasses(classes, fileMap);
295 serializeIndex(classes);
296 serializeToc(classes, fileMap);
297 process.exit(0);
298 }
299 };
300})();