1 | var _ = require('lodash');
|
2 | var fs = require('fs');
|
3 | var os = require('os');
|
4 | var path = require('path');
|
5 | var semver = require('semver');
|
6 |
|
7 |
|
8 | var Filter = require('./filter');
|
9 | var Parser = require('./parser');
|
10 | var Worker = require('./worker');
|
11 |
|
12 | var PluginLoader = require('./plugin_loader');
|
13 |
|
14 | var FileError = require('./errors/file_error');
|
15 | var ParserError = require('./errors/parser_error');
|
16 | var WorkerError = require('./errors/worker_error');
|
17 |
|
18 |
|
19 | var SPECIFICATION_VERSION = '0.3.0';
|
20 |
|
21 | var defaults = {
|
22 | excludeFilters: [],
|
23 | includeFilters: [ '.*\\.(clj|cls|coffee|cpp|cs|dart|erl|exs?|go|groovy|ino?|java|js|jsx|litcoffee|lua|p|php?|pl|pm|py|rb|scala|ts|vue)$' ],
|
24 |
|
25 | src: path.join(__dirname, '../example/'),
|
26 |
|
27 | filters: {},
|
28 | languages: {},
|
29 | parsers: {},
|
30 | workers: {},
|
31 |
|
32 | lineEnding: detectLineEnding(),
|
33 | encoding: 'utf8'
|
34 | };
|
35 |
|
36 | var app = {
|
37 | options : {},
|
38 | log : logger,
|
39 | generator : {},
|
40 | packageInfos: {},
|
41 | markdownParser: false,
|
42 | filters: {
|
43 | apierror : './filters/api_error.js',
|
44 | apiheader : './filters/api_header.js',
|
45 | apiparam : './filters/api_param.js',
|
46 | apisuccess : './filters/api_success.js'
|
47 | },
|
48 | languages: {
|
49 | '.clj' : './languages/clj.js',
|
50 | '.coffee' : './languages/coffee.js',
|
51 | '.erl' : './languages/erl.js',
|
52 | '.ex' : './languages/ex.js',
|
53 | '.exs' : './languages/ex.js',
|
54 | '.litcoffee' : './languages/coffee.js',
|
55 | '.lua' : './languages/lua.js',
|
56 | '.pl' : './languages/pm.js',
|
57 | '.pm' : './languages/pm.js',
|
58 | '.py' : './languages/py.js',
|
59 | '.rb' : './languages/rb.js',
|
60 | 'default' : './languages/default.js'
|
61 | },
|
62 | parsers: {
|
63 | api : './parsers/api.js',
|
64 | apidefine : './parsers/api_define.js',
|
65 | apidescription : './parsers/api_description.js',
|
66 | apierror : './parsers/api_error.js',
|
67 | apierrorexample : './parsers/api_error_example.js',
|
68 | apiexample : './parsers/api_example.js',
|
69 | apiheader : './parsers/api_header.js',
|
70 | apiheaderexample : './parsers/api_header_example.js',
|
71 | apigroup : './parsers/api_group.js',
|
72 | apiname : './parsers/api_name.js',
|
73 | apiparam : './parsers/api_param.js',
|
74 | apiparamexample : './parsers/api_param_example.js',
|
75 | apipermission : './parsers/api_permission.js',
|
76 | apisuccess : './parsers/api_success.js',
|
77 | apisuccessexample : './parsers/api_success_example.js',
|
78 | apiuse : './parsers/api_use.js',
|
79 | apiversion : './parsers/api_version.js',
|
80 | apisamplerequest : './parsers/api_sample_request.js',
|
81 | apideprecated : './parsers/api_deprecated.js'
|
82 | },
|
83 | workers: {
|
84 | apierrorstructure : './workers/api_error_structure.js',
|
85 | apierrortitle : './workers/api_error_title.js',
|
86 | apigroup : './workers/api_group.js',
|
87 | apiheaderstructure : './workers/api_header_structure.js',
|
88 | apiheadertitle : './workers/api_header_title.js',
|
89 | apiname : './workers/api_name.js',
|
90 | apiparamtitle : './workers/api_param_title.js',
|
91 | apipermission : './workers/api_permission.js',
|
92 | apisamplerequest : './workers/api_sample_request.js',
|
93 | apistructure : './workers/api_structure.js',
|
94 | apisuccessstructure : './workers/api_success_structure.js',
|
95 | apisuccesstitle : './workers/api_success_title.js',
|
96 | apiuse : './workers/api_use.js'
|
97 | },
|
98 | hooks: {},
|
99 | addHook: addHook,
|
100 | hook: applyHook
|
101 | };
|
102 |
|
103 | var defaultGenerator = {
|
104 | name : 'apidoc',
|
105 | time : new Date(),
|
106 | url : 'http://apidocjs.com',
|
107 | version: '0.0.0'
|
108 | };
|
109 |
|
110 |
|
111 | var defaultPackageInfos = {
|
112 | description: '',
|
113 | name : '',
|
114 | sampleUrl : false,
|
115 | version : '0.0.0',
|
116 | defaultVersion: '0.0.0'
|
117 | };
|
118 |
|
119 |
|
120 | var logger = {
|
121 | debug : function() { console.log(arguments); },
|
122 | verbose: function() { console.log(arguments); },
|
123 | info : function() { console.log(arguments); },
|
124 | warn : function() { console.log(arguments); },
|
125 | error : function() { console.log(arguments); }
|
126 | };
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | function getSpecificationVersion() {
|
134 | return SPECIFICATION_VERSION;
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | function detectLineEnding() {
|
143 | if ( os.platform() === 'win32' )
|
144 | return '\r\n';
|
145 | if ( os.platform() === 'darwin' )
|
146 | return '\r';
|
147 | return '\n';
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | function parse(options) {
|
163 | options = _.defaults({}, options, defaults);
|
164 |
|
165 |
|
166 | app.filters = _.defaults({}, options.filters, app.filters);
|
167 | app.languages = _.defaults({}, options.languages, app.languages);
|
168 | app.parsers = _.defaults({}, options.parsers, app.parsers);
|
169 | app.workers = _.defaults({}, options.workers, app.workers);
|
170 | app.hooks = _.defaults({}, options.hooks, app.hooks);
|
171 |
|
172 |
|
173 | app.options = options;
|
174 |
|
175 |
|
176 | app.generator = _.defaults({}, app.generator, defaultGenerator);
|
177 |
|
178 |
|
179 | app.packageInfos = _.defaults({}, app.packageInfos, defaultPackageInfos);
|
180 |
|
181 | var parsedFiles = [];
|
182 | var parsedFilenames = [];
|
183 |
|
184 | try {
|
185 |
|
186 | var filename = path.join(__dirname, '../', './package.json');
|
187 | var packageJson = JSON.parse( fs.readFileSync( filename , 'utf8') );
|
188 | app.log.verbose('apidoc-generator name: ' + app.generator.name);
|
189 | app.log.verbose('apidoc-generator version: ' + app.generator.version);
|
190 | app.log.verbose('apidoc-core version: ' + packageJson.version);
|
191 | app.log.verbose('apidoc-spec version: ' + getSpecificationVersion());
|
192 |
|
193 | new PluginLoader(app);
|
194 |
|
195 | var parser = new Parser(app);
|
196 | var worker = new Worker(app);
|
197 | var filter = new Filter(app);
|
198 |
|
199 |
|
200 | app.parser = parser;
|
201 | app.worker = worker;
|
202 | app.filter = filter;
|
203 |
|
204 |
|
205 |
|
206 | app.log.verbose('run parser');
|
207 | if (options.src instanceof Array) {
|
208 | options.src.forEach(function(folder) {
|
209 |
|
210 |
|
211 | var folderOptions = options;
|
212 | folderOptions.src = path.join(folder, './');
|
213 | parser.parseFiles(folderOptions, parsedFiles, parsedFilenames);
|
214 | });
|
215 | }
|
216 | else {
|
217 |
|
218 | options.src = path.join(options.src, './');
|
219 | parser.parseFiles(options, parsedFiles, parsedFilenames);
|
220 | }
|
221 |
|
222 | if (parsedFiles.length > 0) {
|
223 |
|
224 | app.log.verbose('run worker');
|
225 | worker.process(parsedFiles, parsedFilenames, app.packageInfos);
|
226 |
|
227 |
|
228 | app.log.verbose('run filter');
|
229 | var blocks = filter.process(parsedFiles, parsedFilenames);
|
230 |
|
231 |
|
232 | blocks.sort(function(a, b) {
|
233 | var nameA = a.group + a.name;
|
234 | var nameB = b.group + b.name;
|
235 | if (nameA === nameB) {
|
236 | if (a.version === b.version)
|
237 | return 0;
|
238 | return (semver.gte(a.version, b.version)) ? -1 : 1;
|
239 | }
|
240 | return (nameA < nameB) ? -1 : 1;
|
241 | });
|
242 |
|
243 |
|
244 | app.packageInfos.apidoc = SPECIFICATION_VERSION;
|
245 |
|
246 |
|
247 | app.packageInfos.generator = app.generator;
|
248 |
|
249 |
|
250 | var apiData = JSON.stringify(blocks, null, 2);
|
251 | apiData = apiData.replace(/(\r\n|\n|\r)/g, app.options.lineEnding);
|
252 |
|
253 |
|
254 | var apiProject = JSON.stringify(app.packageInfos, null, 2);
|
255 | apiProject = apiProject.replace(/(\r\n|\n|\r)/g, app.options.lineEnding);
|
256 |
|
257 | return {
|
258 | data : apiData,
|
259 | project: apiProject
|
260 | };
|
261 | }
|
262 | return true;
|
263 | } catch(e) {
|
264 |
|
265 | var extra;
|
266 | var meta = {};
|
267 | if (e instanceof FileError) {
|
268 | meta = { 'Path': e.path };
|
269 | app.log.error(e.message, meta);
|
270 | } else if (e instanceof ParserError) {
|
271 | extra = e.extra;
|
272 | if (e.source)
|
273 | extra.unshift({ 'Source': e.source });
|
274 | if (e.element)
|
275 | extra.unshift({ 'Element': '@' + e.element });
|
276 | if (e.block)
|
277 | extra.unshift({ 'Block': e.block });
|
278 | if (e.file)
|
279 | extra.unshift({ 'File': e.file });
|
280 |
|
281 | extra.forEach(function(obj) {
|
282 | var key = Object.keys(obj)[0];
|
283 | meta[key] = obj[key];
|
284 | });
|
285 |
|
286 | app.log.error(e.message, meta);
|
287 | }
|
288 | else if (e instanceof WorkerError) {
|
289 | extra = e.extra;
|
290 | if (e.definition)
|
291 | extra.push({ 'Definition': e.definition });
|
292 | if (e.example)
|
293 | extra.push({ 'Example': e.example });
|
294 | extra.unshift({ 'Element': '@' + e.element });
|
295 | extra.unshift({ 'Block': e.block });
|
296 | extra.unshift({ 'File': e.file });
|
297 |
|
298 | extra.forEach(function(obj) {
|
299 | var key = Object.keys(obj)[0];
|
300 | meta[key] = obj[key];
|
301 | });
|
302 |
|
303 | app.log.error(e.message, meta);
|
304 | }
|
305 | else {
|
306 | app.log.error(e.message);
|
307 | if (e.stack)
|
308 | app.log.debug(e.stack);
|
309 | }
|
310 | return false;
|
311 | }
|
312 | }
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 | function setGeneratorInfos(generator) {
|
324 | app.generator = generator;
|
325 | }
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 | function setLogger(logger) {
|
339 | app.log = logger;
|
340 | }
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 | function setMarkdownParser(markdownParser) {
|
348 | app.markdownParser = markdownParser;
|
349 | }
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 | function setPackageInfos(packageInfos) {
|
361 | app.packageInfos = packageInfos;
|
362 | }
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 | function addHook(name, func, priority) {
|
373 | priority = priority || 100;
|
374 |
|
375 | if ( ! app.hooks[name])
|
376 | app.hooks[name] = [];
|
377 |
|
378 | app.log.debug('add hook: ' + name + ' [' + priority + ']');
|
379 |
|
380 |
|
381 | var replace = 0;
|
382 | var pos = 0;
|
383 | app.hooks[name].forEach( function(entry, index) {
|
384 | if (priority === entry.priority) {
|
385 | pos = index;
|
386 | replace = 1;
|
387 | } else if (priority > entry.priority) {
|
388 | pos = index + 1;
|
389 | }
|
390 | });
|
391 |
|
392 | app.hooks[name].splice(pos, replace, {
|
393 | func: func,
|
394 | priority: priority
|
395 | });
|
396 | }
|
397 |
|
398 |
|
399 |
|
400 |
|
401 | function applyHook(name /* , ...args */) {
|
402 | if ( ! app.hooks[name])
|
403 | return Array.prototype.slice.call(arguments, 1, 2)[0];
|
404 |
|
405 | var args = Array.prototype.slice.call(arguments, 1);
|
406 | app.hooks[name].forEach( function(hook) {
|
407 | hook.func.apply(this, args);
|
408 | });
|
409 | return args[0];
|
410 | }
|
411 |
|
412 |
|
413 | module.exports = {
|
414 | getSpecificationVersion: getSpecificationVersion,
|
415 | parse : parse,
|
416 | setGeneratorInfos : setGeneratorInfos,
|
417 | setLogger : setLogger,
|
418 | setMarkdownParser : setMarkdownParser,
|
419 | setPackageInfos : setPackageInfos
|
420 | };
|