Skip to content

Commit

Permalink
Add Plugin Loader. Extend filter, parser, worker for plugin usage. Ad…
Browse files Browse the repository at this point in the history
…d hooks for found elements.
  • Loading branch information
rottmann committed Apr 21, 2016
1 parent e13e06b commit 20921ef
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 15 deletions.
33 changes: 33 additions & 0 deletions hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# apiDoc Hooks


## parser-find-elements

Called on each found element. Returns a new list of elements (replace elements).
Used to inject annotationes from an external schema.

Parameter: `(elements, element, block, filename)`
* {array} elements Found elements in a block without the current element.
* {array} element Contains the source, name (lowercase), sourceName (original), content.
* {string} block Current source block.
* {string} filename Current filename.

File: `parser.js`
Function: `_findElements`



## parser-find-element-{name}

Called on each found element and returns the modified element.
Used to modify a specific element.

{name} is the found element.name (lowercase).

Parameter: `(element, block, filename)`
* {array} element Contains the source, name (lowercase), sourceName (original), content.
* {string} block Current source block.
* {string} filename Current filename.

File: `parser.js`
Function: `_findElements`
11 changes: 8 additions & 3 deletions lib/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ function Filter(_app) {
// load filters
var filters = Object.keys(app.filters);
filters.forEach(function(filter) {
var filename = app.filters[filter];
app.log.debug('load filter: ' + filter + ', ' + filename);
self.addFilter(filter, require(filename));
if (_.isObject( app.filters[filter] )) {
app.log.debug('inject filter: ' + parser);
self.addFilter(worker, app.filters[filter] );
} else {
var filename = app.filters[filter];
app.log.debug('load filter: ' + filter + ', ' + filename);
self.addFilter(filter, require(filename));
}
});
}

Expand Down
64 changes: 63 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ 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');
Expand Down Expand Up @@ -90,7 +92,10 @@ var app = {
apisuccessstructure : './workers/api_success_structure.js',
apisuccesstitle : './workers/api_success_title.js',
apiuse : './workers/api_use.js'
}
},
hooks: {},
addHook: addHook,
hook: applyHook
};

var defaultGenerator = {
Expand Down Expand Up @@ -159,6 +164,7 @@ function parse(options) {
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;
Expand All @@ -181,10 +187,17 @@ function parse(options) {
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');
Expand Down Expand Up @@ -345,6 +358,55 @@ 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,
Expand Down
30 changes: 22 additions & 8 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,27 @@ function Parser(_app) {
// load languages
var languages = Object.keys(app.languages);
languages.forEach(function(language) {
var filename = app.languages[language];
app.log.debug('load parser language: ' + language + ', ' + filename);
self.addLanguage(language, require(filename));
if (_.isObject( app.languages[language] )) {
app.log.debug('inject parser language: ' + language);
self.addLanguage(language, app.languages[language] );
} else {
var filename = app.languages[language];
app.log.debug('load parser language: ' + language + ', ' + filename);
self.addLanguage(language, require(filename));
}
});

// load parser
var parsers = Object.keys(app.parsers);
parsers.forEach(function(parser) {
var filename = app.parsers[parser];
app.log.debug('load parser: ' + parser + ', ' + filename);
self.addParser(parser, require(filename));
if (_.isObject( app.parsers[parser] )) {
app.log.debug('inject parser: ' + parser);
self.addParser(parser, app.parsers[parser] );
} else {
var filename = app.parsers[parser];
app.log.debug('load parser: ' + parser + ', ' + filename);
self.addParser(parser, require(filename));
}
});
}

Expand Down Expand Up @@ -127,7 +137,7 @@ Parser.prototype.parseFile = function(filename, encoding) {

// determine elements in blocks
self.elements = self.blocks.map(function(block, i) {
var elements = self._findElements(block);
var elements = self.findElements(block, filename);
app.log.debug('count elements in block ' + i + ': ' + elements.length);
return elements;
});
Expand Down Expand Up @@ -420,7 +430,7 @@ Parser.prototype._findBlockWithApiGetIndex = function(blocks) {
/**
* Get Elements of Blocks
*/
Parser.prototype._findElements = function(block) {
Parser.prototype.findElements = function(block, filename) {
var elements = [];

// Replace Linebreak with Unicode
Expand All @@ -441,8 +451,12 @@ Parser.prototype._findElements = function(block) {
element.content = element.content.replace(/\uffff/g, '\n');
element.source = element.source.replace(/\uffff/g, '\n');

app.hook('parser-find-element-' + element.name, element, block, filename);

elements.push(element);

app.hook('parser-find-elements', elements, element, block, filename);

// next Match
matches = elementsRegExp.exec(block);
}
Expand Down
92 changes: 92 additions & 0 deletions lib/plugin_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var _ = require('lodash');
var fs = require('fs');
var path = require('path');
var util = require('util');
var glob = require('glob');

var app = {};

function PluginLoader(_app) {
var self = this;

// global variables
app = _app;

// class variables
self.plugins = {};

// Try to load global apidoc-plugins (if apidoc is installed locally it tries only local)
this.detectPugins(__dirname);

// Try to load local apidoc-plugins
this.detectPugins( path.join(process.cwd(), '/node_modules') );

if (Object.keys(this.plugins).length === 0)
app.log.debug('No plugins found.');

this.loadPlugins();
}
/**
* Inherit
*/
util.inherits(PluginLoader, Object);

/**
* Exports
*/
module.exports = PluginLoader;

/**
* Detect modules start with "apidoc-plugin-".
* Search up to root until found a plugin.
*/
PluginLoader.prototype.detectPugins = function(dir) {
var self = this;

// Search from the given dir up to root.
// Every dir start with "apidoc-plugin-", because for the tests of apidoc-plugin-test.
var plugins = glob.sync(dir + '/apidoc-plugin-*');
if (plugins.length === 0) {
dir = path.join(dir, '..');
if (dir === '/')
return;
return this.detectPugins(dir);
}

var offset = dir.length + 1;
plugins.forEach( function(plugin) {
var name = plugin.substr(offset);
var filename = path.relative(__dirname, plugin);
app.log.debug('add plugin: ' + name + ', ' + filename);
self.addPlugin(name, plugin);
});
};

/**
* Add Plugin to plugin list.
*/
PluginLoader.prototype.addPlugin = function(name, filename) {
if (this.plugins[name])
app.log.debug('overwrite plugin: ' + name + ', ' + this.plugins[name]);

this.plugins[name] = filename;
};

/**
* Load and initialize Plugins.
*/
PluginLoader.prototype.loadPlugins = function() {
_.forEach(this.plugins, function(filename, name) {
app.log.debug('load plugin: ' + name + ', ' + filename);
var plugin;
try {
plugin = require(filename);
} catch(e) {
}
if (plugin && plugin.init) {
plugin.init(app);
} else {
app.log.debug('Ignored, no init function found.');
}
});
};
11 changes: 8 additions & 3 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ function Worker(_app) {
// load worker
var workers = Object.keys(app.workers);
workers.forEach(function(worker) {
var filename = app.workers[worker];
app.log.debug('load worker: ' + worker + ', ' + filename);
self.addWorker(worker, require(filename));
if (_.isObject( app.workers[worker] )) {
app.log.debug('inject worker: ' + parser);
self.addWorker(worker, app.workers[worker] );
} else {
var filename = app.workers[worker];
app.log.debug('load worker: ' + worker + ', ' + filename);
self.addWorker(worker, require(filename));
}
});
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"node": ">= 0.10.0"
},
"dependencies": {
"glob": "^7.0.3",
"iconv-lite": "^0.4.13",
"lodash": "~4.5.0",
"semver": "~5.1.0",
Expand Down

0 comments on commit 20921ef

Please sign in to comment.