UNPKG

12.4 kBJavaScriptView Raw
1var _ = require('lodash');
2var path = require('path');
3var semver = require('semver');
4
5/*jshint -W079 */
6var Filter = require('./filter');
7var Parser = require('./parser');
8var Worker = require('./worker');
9
10var FileError = require('./errors/file_error');
11var ParserError = require('./errors/parser_error');
12var WorkerError = require('./errors/worker_error');
13
14// const
15var SPECIFICATION_VERSION = '0.2.0';
16
17var defaults = {
18 excludeFilters: [],
19 includeFilters: [ '.*\\.(clj|coffee|cs|dart|erl|go|java|js|php?|py|rb|ts|pm)$' ],
20
21 src: path.join(__dirname, '../example/'),
22
23 filters: {},
24 languages: {},
25 parsers: {},
26 workers: {}
27};
28
29var app = {
30 options : {},
31 log : logger,
32 generator : {},
33 packageInfos: {},
34 markdown : false,
35 filters: {
36 apierror : './filters/api_error.js',
37 apiheader : './filters/api_header.js',
38 apiparam : './filters/api_param.js',
39 apisuccess : './filters/api_success.js'
40 },
41 languages: {
42 '.clj' : './languages/clj.js',
43 '.coffee' : './languages/coffee.js',
44 '.erl' : './languages/erl.js',
45 '.pm' : './languages/pm.js',
46 '.py' : './languages/py.js',
47 '.rb' : './languages/rb.js',
48 'default' : './languages/default.js'
49 },
50 parsers: {
51 api : './parsers/api.js',
52 apidefine : './parsers/api_define.js',
53 apidefineerrorstructure : './parsers/api_define_error_structure.js',
54 apidefineheaderstructure : './parsers/api_define_header_structure.js',
55 apidefinepermission : './parsers/api_define_permission.js',
56 apidefinestructure : './parsers/api_define_structure.js',
57 apidefinesuccessstructure: './parsers/api_define_success_structure.js',
58 apigroupdescription : './parsers/api_group_description.js',
59 apidescription : './parsers/api_description.js',
60 apierror : './parsers/api_error.js',
61 apierrorexample : './parsers/api_error_example.js',
62 apierrorstructure : './parsers/api_error_structure.js',
63 apierrortitle : './parsers/api_error_title.js',
64 apiexample : './parsers/api_example.js',
65 apiheader : './parsers/api_header.js',
66 apiheaderexample : './parsers/api_header_example.js',
67 apiheaderstructure : './parsers/api_header_structure.js',
68 apiheadertitle : './parsers/api_header_title.js',
69 apigroup : './parsers/api_group.js',
70 apiname : './parsers/api_name.js',
71 apiparam : './parsers/api_param.js',
72 apiparamexample : './parsers/api_param_example.js',
73 apiparamtitle : './parsers/api_param_title.js',
74 apipermission : './parsers/api_permission.js',
75 apistructure : './parsers/api_structure.js',
76 apisuccess : './parsers/api_success.js',
77 apisuccessexample : './parsers/api_success_example.js',
78 apisuccessstructure : './parsers/api_success_structure.js',
79 apisuccesstitle : './parsers/api_success_title.js',
80 apiuse : './parsers/api_use.js',
81 apiversion : './parsers/api_version.js',
82 apisamplerequest : './parsers/api_sample_request.js'
83 },
84 workers: {
85 apierrorstructure : './workers/api_error_structure.js',
86 apierrortitle : './workers/api_error_title.js',
87 apigroup : './workers/api_group.js',
88 apiheaderstructure : './workers/api_header_structure.js',
89 apiheadertitle : './workers/api_header_title.js',
90 apiname : './workers/api_name.js',
91 apiparamtitle : './workers/api_param_title.js',
92 apipermission : './workers/api_permission.js',
93 apisamplerequest : './workers/api_sample_request.js',
94 apistructure : './workers/api_structure.js',
95 apisuccessstructure : './workers/api_success_structure.js',
96 apisuccesstitle : './workers/api_success_title.js',
97 apiuse : './workers/api_use.js',
98
99 deprecatedApiErrorTitle : './workers/deprecated_api_error_title.js',
100 deprecatedApiHeaderTitle : './workers/deprecated_api_header_title.js',
101 deprecatedApiParamTitle : './workers/deprecated_api_param_title.js',
102 deprecatedApiSuccessTitle: './workers/deprecated_api_success_title.js'
103 }
104};
105
106var defaultGenerator = {
107 name : 'apidoc',
108 time : new Date(),
109 url : 'http://apidocjs.com',
110 version: '0.0.0'
111};
112
113// TODO: find abetter name for PackageInfos (-> apidoc-conf)
114var defaultPackageInfos = {
115 description: '',
116 name : '',
117 sampleUrl : false,
118 version : '0.0.0'
119};
120
121// Simple logger interace
122var logger = {
123 debug : function() { console.log(arguments); },
124 verbose: function() { console.log(arguments); },
125 info : function() { console.log(arguments); },
126 warn : function() { console.log(arguments); },
127 error : function() { console.log(arguments); }
128};
129
130/**
131 * Return the used specification version
132 *
133 * @returns {String}
134 */
135function getSpecificationVersion() {
136 return SPECIFICATION_VERSION;
137}
138
139/**
140 * Parser
141 *
142 * @param {Object} options Overwrite default options.
143 * @param {Object} logger Logger (with methods: debug, verbose, info, warn and error is necessary).
144
145 * @returns {Mixed} true = ok, but nothing todo | false = error | Object with parsed data and project-informations.
146 * {
147 * data : { ... }
148 * project: { ... }
149 * }
150 */
151function parse(options) {
152 _.defaults(options, defaults);
153
154 // extend with custom functions
155 app.filters = _.defaults(options.filters, app.filters);
156 app.languages = _.defaults(options.languages, app.languages);
157 app.parsers = _.defaults(options.parsers, app.parsers);
158 app.workers = _.defaults(options.workers, app.workers);
159
160 // options
161 app.options = options;
162
163 // generator
164 _.defaults(app.generator, defaultGenerator);
165
166 // packageInfos
167 _.defaults(app.packageInfos, defaultPackageInfos);
168
169 var parsedFiles = [];
170 var parsedFilenames = [];
171
172 try {
173 var parser = new Parser(app);
174 var worker = new Worker(app);
175 var filter = new Filter(app);
176
177 // if input option for source is an array of folders,
178 // parse each folder in the order provided.
179 if (options.src instanceof Array) {
180 options.src.forEach(function(folder) {
181 // Keep same options for each folder, but ensure the 'src' of options
182 // is the folder currently being processed.
183 var folderOptions = options;
184 folderOptions.src = path.join(folder, './');
185 parser.parseFiles(folderOptions, parsedFiles, parsedFilenames);
186 });
187 }
188 else {
189 // if the input option for source is a single folder, parse as usual.
190 options.src = path.join(options.src, './');
191 parser.parseFiles(options, parsedFiles, parsedFilenames);
192 }
193
194 if (parsedFiles.length > 0) {
195 // process transformations and assignments
196 worker.process(parsedFiles, parsedFilenames, app.packageInfos);
197
198 // cleanup
199 var blocks = filter.process(parsedFiles, parsedFilenames);
200
201 // sort by group ASC, name ASC, version DESC
202 blocks.sort(function(a, b) {
203 var nameA = a.group + a.name;
204 var nameB = b.group + b.name;
205 if (nameA === nameB) {
206 if (a.version === b.version)
207 return 0;
208 return (semver.gte(a.version, b.version)) ? -1 : 1;
209 }
210 return (nameA < nameB) ? -1 : 1;
211 });
212
213 // add apiDoc specification version
214 app.packageInfos.apidoc = SPECIFICATION_VERSION;
215
216 // add apiDoc specification version
217 app.packageInfos.generator = app.generator;
218
219 // api_data
220 var apiData = JSON.stringify(blocks, null, 2);
221 apiData = apiData.replace(/(\r\n|\n|\r)/g, '\r\n');
222
223 // api_project
224 var apiProject = JSON.stringify(app.packageInfos, null, 2);
225 apiProject = apiProject.replace(/(\r\n|\n|\r)/g, '\r\n');
226
227 return {
228 data : apiData,
229 project: apiProject
230 };
231 }
232 return true;
233 } catch(e) {
234 // display error by instance
235 var extra;
236 var meta = {};
237 if (e instanceof FileError) {
238 meta = { 'Path': e.path };
239 app.log.error(e.message, meta);
240 } else if (e instanceof ParserError) {
241 extra = e.extra;
242 if (e.source)
243 extra.unshift({ 'Source': e.source });
244 if (e.element)
245 extra.unshift({ 'Element': '@' + e.element });
246 if (e.block)
247 extra.unshift({ 'Block': e.block });
248 if (e.file)
249 extra.unshift({ 'File': e.file });
250
251 extra.forEach(function(obj) {
252 var key = Object.keys(obj)[0];
253 meta[key] = obj[key];
254 });
255
256 app.log.error(e.message, meta);
257 }
258 else if (e instanceof WorkerError) {
259 extra = e.extra;
260 if (e.definition)
261 extra.push({ 'Definition': e.definition });
262 if (e.example)
263 extra.push({ 'Example': e.example });
264 extra.unshift({ 'Element': '@' + e.element });
265 extra.unshift({ 'Block': e.block });
266 extra.unshift({ 'File': e.file });
267
268 extra.forEach(function(obj) {
269 var key = Object.keys(obj)[0];
270 meta[key] = obj[key];
271 });
272
273 app.log.error(e.message, meta);
274 }
275 else {
276 app.log.error(e.message);
277 if (e.stack)
278 app.log.debug(e.stack);
279 }
280 return false;
281 }
282}
283
284/**
285 * Set generator informations.
286 *
287 * @param {Object} [generator] Generator informations.
288 * @param {String} [generator.name] Generator name (UI-Name).
289 * @param {String} [generator.time] Time for the generated doc
290 * @param {String} [generator.version] Version (semver) of the generator, e.g. 1.2.3
291 * @param {String} [generator.url] Url to the generators homepage
292 */
293function setGeneratorInfos(generator) {
294 app.generator = generator;
295}
296
297/**
298 * Set a logger.
299 *
300 * @param {Object} logger A Logger (@see https://github.com/flatiron/winston for details)
301 * Interface:
302 * debug(msg, meta)
303 * verbose(msg, meta)
304 * info(msg, meta)
305 * warn(msg, meta)
306 * error(msg, meta)
307 */
308function setLogger(logger) {
309 app.log = logger;
310}
311
312/**
313 * Set the markdown parser.
314 *
315 * @param {Object} [markdown] Markdown parser.
316 */
317function setMarkdownParser(markdown) {
318 app.markdown = markdown;
319}
320
321/**
322 * Set package infos.
323 *
324 * @param {Object} [packageInfos] Collected from apidoc.json / package.json.
325 * @param {String} [packageInfos.name] Project name.
326 * @param {String} [packageInfos.version] Version (semver) of the project, e.g. 1.0.27
327 * @param {String} [packageInfos.description] A short description.
328 * @param {String} [packageInfos.sampleUrl] @see http://apidocjs.com/#param-api-sample-request
329 */
330function setPackageInfos(packageInfos) {
331 app.packageInfos = packageInfos;
332}
333
334module.exports = {
335 getSpecificationVersion: getSpecificationVersion,
336 parse : parse,
337 setGeneratorInfos : setGeneratorInfos,
338 setLogger : setLogger,
339 setMarkdownParser : setMarkdownParser,
340 setPackageInfos : setPackageInfos
341};