123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- /*
- * apidoc
- * https://apidocjs.com
- *
- * Authors:
- * Peter Rottmann <rottmann@inveris.de>
- * Nicolas CARPi @ Deltablot
- * Copyright (c) 2013 inveris OHG
- * Licensed under the MIT license.
- */
- const _ = require('lodash');
- const os = require('os');
- const path = require('path');
- const semver = require('semver');
- const Filter = require('./filter');
- const Parser = require('./parser');
- const Worker = require('./worker');
- const PluginLoader = require('./plugin_loader');
- const FileError = require('./errors/file_error');
- const ParserError = require('./errors/parser_error');
- const WorkerError = require('./errors/worker_error');
- // const
- const SPECIFICATION_VERSION = '0.3.0';
- const defaults = {
- excludeFilters: [],
- 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)$'],
- src: path.join(__dirname, '../../example/'),
- filters: {},
- languages: {},
- parsers: {},
- workers: {},
- lineEnding: os.EOL,
- encoding: 'utf8',
- };
- // Simple logger interace
- const logger = {
- debug: function () { console.log(arguments); },
- verbose: function () { console.log(arguments); },
- info: function () { console.log(arguments); },
- warn: function () { console.log(arguments); },
- error: function () { console.log(arguments); },
- };
- const app = {
- options: {}, // see defaults
- log: logger,
- generator: {},
- packageInfos: {},
- markdownParser: false,
- filters: {
- apierror: './filters/api_error.js',
- apiheader: './filters/api_header.js',
- apiparam: './filters/api_param.js',
- apisuccess: './filters/api_success.js',
- },
- languages: {
- '.clj': './languages/clj.js',
- '.coffee': './languages/coffee.js',
- '.erl': './languages/erl.js',
- '.ex': './languages/ex.js',
- '.exs': './languages/ex.js',
- '.litcoffee': './languages/coffee.js',
- '.lua': './languages/lua.js',
- '.pl': './languages/pm.js',
- '.pm': './languages/pm.js',
- '.py': './languages/py.js',
- '.rb': './languages/rb.js',
- default: './languages/default.js',
- },
- parsers: {
- api: './parsers/api.js',
- apibody: './parsers/api_body.js',
- apiquery: './parsers/api_query.js',
- apidefine: './parsers/api_define.js',
- apidescription: './parsers/api_description.js',
- apierror: './parsers/api_error.js',
- apierrorexample: './parsers/api_error_example.js',
- apiexample: './parsers/api_example.js',
- apiheader: './parsers/api_header.js',
- apiheaderexample: './parsers/api_header_example.js',
- apigroup: './parsers/api_group.js',
- apiname: './parsers/api_name.js',
- apiparam: './parsers/api_param.js',
- apiparamexample: './parsers/api_param_example.js',
- apipermission: './parsers/api_permission.js',
- apisuccess: './parsers/api_success.js',
- apisuccessexample: './parsers/api_success_example.js',
- apiuse: './parsers/api_use.js',
- apiversion: './parsers/api_version.js',
- apisamplerequest: './parsers/api_sample_request.js',
- apideprecated: './parsers/api_deprecated.js',
- apiprivate: './parsers/api_private.js',
- },
- workers: {
- apierrorstructure: './workers/api_error_structure.js',
- apierrortitle: './workers/api_error_title.js',
- apigroup: './workers/api_group.js',
- apiheaderstructure: './workers/api_header_structure.js',
- apiheadertitle: './workers/api_header_title.js',
- apiname: './workers/api_name.js',
- apiparamtitle: './workers/api_param_title.js',
- apipermission: './workers/api_permission.js',
- apisamplerequest: './workers/api_sample_request.js',
- apistructure: './workers/api_structure.js',
- apisuccessstructure: './workers/api_success_structure.js',
- apisuccesstitle: './workers/api_success_title.js',
- apiuse: './workers/api_use.js',
- },
- hooks: {},
- addHook: addHook,
- hook: applyHook,
- };
- const defaultGenerator = {
- name: 'apidoc',
- time: new Date().toString(),
- url: 'https://apidocjs.com',
- version: '0.0.0',
- };
- // default apidoc.conf values
- const defaultApidocConf = {
- description: 'API Documentation',
- name: '',
- sampleUrl: false,
- version: '0.0.0',
- defaultVersion: '0.0.0',
- };
- /**
- * Return the used specification version
- *
- * @returns {String}
- */
- function getSpecificationVersion () {
- return SPECIFICATION_VERSION;
- }
- /**
- * Parser
- *
- * @param {Object} options Overwrite default options.
- * @returns {Boolean|Object} true = ok, but nothing todo | false = error | Object with parsed data and project-informations.
- * {
- * data : { ... }
- * project: { ... }
- * }
- */
- function parse (options) {
- try {
- initApp(options);
- options = app.options;
- const parsedFiles = [];
- const parsedFilenames = [];
- // if input option for source is an array of folders,
- // parse each folder in the order provided.
- app.log.verbose('run parser');
- if (options.src instanceof Array) {
- options.src.forEach(function (folder) {
- // Keep same options for each folder, but ensure the 'src' of options
- // is the folder currently being processed.
- const folderOptions = options;
- folderOptions.src = path.join(folder, './');
- app.parser.parseFiles(folderOptions, parsedFiles, parsedFilenames);
- });
- } else {
- // if the input option for source is a single folder, parse as usual.
- options.src = path.join(options.src, './');
- app.parser.parseFiles(options, parsedFiles, parsedFilenames);
- }
- if (parsedFiles.length > 0) {
- // process transformations and assignments
- app.log.verbose('run worker');
- app.worker.process(parsedFiles, parsedFilenames, app.packageInfos);
- // cleanup
- app.log.verbose('run filter');
- const blocks = app.filter.process(parsedFiles, parsedFilenames);
- // sort by group ASC, name ASC, version DESC
- blocks.sort(function (a, b) {
- const nameA = a.group + a.name;
- const nameB = b.group + b.name;
- if (nameA === nameB) {
- if (a.version === b.version) { return 0; }
- return semver.gte(a.version, b.version) ? -1 : 1;
- }
- return nameA < nameB ? -1 : 1;
- });
- // add apiDoc specification version
- app.packageInfos.apidoc = SPECIFICATION_VERSION;
- // add apiDoc specification version
- app.packageInfos.generator = app.generator;
- // api_data
- let apiData = JSON.stringify(blocks, null, 2);
- apiData = apiData.replace(/(\r\n|\n|\r)/g, app.options.lineEnding);
- // api_project
- let apiProject = JSON.stringify(app.packageInfos, null, 2);
- apiProject = apiProject.replace(/(\r\n|\n|\r)/g, app.options.lineEnding);
- return {
- data: apiData,
- project: apiProject,
- };
- }
- return true;
- } catch (e) {
- // display error by instance
- let extra;
- let meta = {};
- if (e instanceof FileError) {
- meta = { Path: e.path };
- app.log.error(e.message, meta);
- } else if (e instanceof ParserError) {
- extra = e.extra;
- if (e.source) { extra.unshift({ Source: e.source }); }
- if (e.element) { extra.unshift({ Element: '@' + e.element }); }
- if (e.block) { extra.unshift({ Block: e.block }); }
- if (e.file) { extra.unshift({ File: e.file }); }
- extra.forEach(function (obj) {
- const key = Object.keys(obj)[0];
- meta[key] = obj[key];
- });
- app.log.error(e.message, meta);
- } else if (e instanceof WorkerError) {
- extra = e.extra;
- if (e.definition) { extra.push({ Definition: e.definition }); }
- if (e.example) { extra.push({ Example: e.example }); }
- extra.unshift({ Element: '@' + e.element });
- extra.unshift({ Block: e.block });
- extra.unshift({ File: e.file });
- extra.forEach(function (obj) {
- const key = Object.keys(obj)[0];
- meta[key] = obj[key];
- });
- app.log.error(e.message, meta);
- } else {
- app.log.error(e.message);
- if (e.stack) { app.log.debug(e.stack); }
- }
- return false;
- }
- }
- /**
- * parseSource
- *
- * @param {String} source the source code string.
- * @param {Object} options Overwrite default options.
- */
- function parseSource (source, options) {
- try {
- initApp(options);
- return app.parser.parseSource(source, app.options.encoding, app.options.filename);
- } catch (e) {
- app.log.error(e.message);
- }
- }
- /**
- * initApp
- *
- * @param {Object} options Overwrite default options.
- */
- function initApp (options) {
- options = _.defaults({}, options, defaults);
- // extend with custom functions
- app.filters = _.defaults({}, options.filters, app.filters);
- app.languages = _.defaults({}, options.languages, app.languages);
- app.parsers = _.defaults({}, options.parsers, app.parsers);
- app.workers = _.defaults({}, options.workers, app.workers);
- app.hooks = _.defaults({}, options.hooks, app.hooks);
- // options
- app.options = options;
- // generator
- app.generator = _.defaults({}, app.generator, defaultGenerator);
- // packageInfos
- app.packageInfos = _.defaults({}, app.packageInfos, defaultApidocConf);
- // Log version information
- const pkgjson = require(path.join(__dirname, '../../', 'package.json'));
- app.log.verbose('apidoc-generator name: ' + app.generator.name);
- app.log.verbose('apidoc-generator version: ' + app.generator.version);
- app.log.verbose('apidoc version: ' + pkgjson.version);
- app.log.verbose('apidoc-spec version: ' + getSpecificationVersion());
- new PluginLoader(app); // eslint-disable-line no-new
- const parser = new Parser(app);
- const worker = new Worker(app);
- const filter = new Filter(app);
- // Make them available for plugins
- app.parser = parser;
- app.worker = worker;
- app.filter = filter;
- }
- /**
- * Set generator informations.
- *
- * @param {Object} [generator] Generator informations.
- * @param {String} [generator.name] Generator name (UI-Name).
- * @param {String} [generator.time] Time for the generated doc
- * @param {String} [generator.version] Version (semver) of the generator, e.g. 1.2.3
- * @param {String} [generator.url] Url to the generators homepage
- */
- function setGeneratorInfos (generator) {
- app.generator = generator;
- }
- /**
- * Set a logger.
- *
- * @param {Object} logger A Logger (@see https://github.com/winstonjs/winston for details)
- * Interface:
- * debug(msg, meta)
- * verbose(msg, meta)
- * info(msg, meta)
- * warn(msg, meta)
- * error(msg, meta)
- */
- function setLogger (logger) {
- app.log = logger;
- }
- /**
- * Set the markdown parser.
- *
- * @param {Object} [markdownParser] Markdown parser.
- */
- function setMarkdownParser (markdownParser) {
- app.markdownParser = markdownParser;
- }
- /**
- * Set package infos.
- *
- * @param {Object} [packageInfos] Collected from apidoc.json / package.json.
- * @param {String} [packageInfos.name] Project name.
- * @param {String} [packageInfos.version] Version (semver) of the project, e.g. 1.0.27
- * @param {String} [packageInfos.description] A short description.
- * @param {String} [packageInfos.sampleUrl] @see https://apidocjs.com/#param-api-sample-request
- */
- function setPackageInfos (packageInfos) {
- app.packageInfos = packageInfos;
- }
- /**
- * Register a hook function.
- *
- * @param {String} name Name of the hook. Hook overview: https://github.com/apidoc/apidoc/wiki/Hooks
- * @param {Function} func Callback function.
- * @param {Number} [priority=100] Hook priority. Lower value will be executed first.
- * Same value overwrite a previously defined hook.
- */
- function addHook (name, func, priority) {
- priority = priority || 100;
- if (!app.hooks[name]) { app.hooks[name] = []; }
- app.log.debug('add hook: ' + name + ' [' + priority + ']');
- // Find position and overwrite same priority
- let replace = 0;
- let pos = 0;
- app.hooks[name].forEach(function (entry, index) {
- if (priority === entry.priority) {
- pos = index;
- replace = 1;
- } else if (priority > entry.priority) {
- pos = index + 1;
- }
- });
- app.hooks[name].splice(pos, replace, {
- func: func,
- priority: priority,
- });
- }
- /**
- * Execute a hook.
- */
- function applyHook (name /* , ...args */) {
- if (!app.hooks[name]) { return Array.prototype.slice.call(arguments, 1, 2)[0]; }
- const args = Array.prototype.slice.call(arguments, 1);
- app.hooks[name].forEach(function (hook) {
- hook.func.apply(this, args);
- });
- return args[0];
- }
- module.exports = {
- getSpecificationVersion: getSpecificationVersion,
- parse: parse,
- parseSource: parseSource,
- setGeneratorInfos: setGeneratorInfos,
- setLogger: setLogger,
- setMarkdownParser: setMarkdownParser,
- setPackageInfos: setPackageInfos,
- };
|