123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- var _ = require('lodash');
- var fs = require('fs');
- var os = require('os');
- var path = require('path');
- var semver = require('semver');
- /*jshint -W079 */
- var Filter = require('./filter');
- var Parser = require('./parser');
- var Worker = require('./worker');
- var PluginLoader = require('./plugin_loader');
- var FileError = require('./errors/file_error');
- var ParserError = require('./errors/parser_error');
- var WorkerError = require('./errors/worker_error');
- // const
- var SPECIFICATION_VERSION = '0.3.0';
- var 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: detectLineEnding(),
- encoding: 'utf8'
- };
- var 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',
- 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'
- },
- 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
- };
- var defaultGenerator = {
- name : 'apidoc',
- time : new Date(),
- url : 'http://apidocjs.com',
- version: '0.0.0'
- };
- // TODO: find abetter name for PackageInfos (-> apidoc-conf)
- var defaultPackageInfos = {
- description: '',
- name : '',
- sampleUrl : false,
- version : '0.0.0',
- defaultVersion: '0.0.0'
- };
- // Simple logger interace
- var 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); }
- };
- /**
- * Return the used specification version
- *
- * @returns {String}
- */
- function getSpecificationVersion() {
- return SPECIFICATION_VERSION;
- }
- /**
- * Detect and return OS specific line ending.
- *
- * @returns {String}
- */
- function detectLineEnding() {
- if ( os.platform() === 'win32' )
- return '\r\n';
- if ( os.platform() === 'darwin' )
- return '\r';
- return '\n';
- }
- /**
- * Parser
- *
- * @param {Object} options Overwrite default options.
- * @param {Object} logger Logger (with methods: debug, verbose, info, warn and error is necessary).
- * @returns {Mixed} true = ok, but nothing todo | false = error | Object with parsed data and project-informations.
- * {
- * data : { ... }
- * project: { ... }
- * }
- */
- function parse(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, defaultPackageInfos);
- var parsedFiles = [];
- var parsedFilenames = [];
- try {
- // Log version information
- var filename = path.join(__dirname, '../', './package.json');
- var packageJson = JSON.parse( fs.readFileSync( filename , 'utf8') );
- app.log.verbose('apidoc-generator name: ' + app.generator.name);
- app.log.verbose('apidoc-generator version: ' + app.generator.version);
- app.log.verbose('apidoc-core version: ' + packageJson.version);
- app.log.verbose('apidoc-spec version: ' + getSpecificationVersion());
- new PluginLoader(app);
- var parser = new Parser(app);
- var worker = new Worker(app);
- var filter = new Filter(app);
- // Make them available for plugins
- app.parser = parser;
- app.worker = worker;
- app.filter = filter;
- // 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.
- var folderOptions = options;
- folderOptions.src = path.join(folder, './');
- 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, './');
- parser.parseFiles(options, parsedFiles, parsedFilenames);
- }
- if (parsedFiles.length > 0) {
- // process transformations and assignments
- app.log.verbose('run worker');
- worker.process(parsedFiles, parsedFilenames, app.packageInfos);
- // cleanup
- app.log.verbose('run filter');
- var blocks = filter.process(parsedFiles, parsedFilenames);
- // sort by group ASC, name ASC, version DESC
- blocks.sort(function(a, b) {
- var nameA = a.group + a.name;
- var 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
- var apiData = JSON.stringify(blocks, null, 2);
- apiData = apiData.replace(/(\r\n|\n|\r)/g, app.options.lineEnding);
- // api_project
- var 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
- var extra;
- var 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) {
- var 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) {
- var 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;
- }
- }
- /**
- * 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/flatiron/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 http://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-core/hooks.md
- * @param {Function} func Callback function.
- * @param {Integer} [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
- var replace = 0;
- var 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];
- var 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,
- setGeneratorInfos : setGeneratorInfos,
- setLogger : setLogger,
- setMarkdownParser : setMarkdownParser,
- setPackageInfos : setPackageInfos
- };
|