documentation/lib/output/util/linker_stack.js
Tom MacWright 631c6925d4 feat(core): Switch to Promises everywhere. Adopt Node v4 ES6 (#648)
* feat(core): Switch to Promises everywhere. Adopt Node v4 ES6

Big changes:

* Uses template strings where appropriate
* Config and argument parsing is unified and there is no such thing
  as formatterOptions anymore. All user-passed options go through
  mergeConfig.
* The node API surface changed (again): `buildSync` is removed,
  building operations return Promises.
* Now using Flow for internal type annotations.

More changes:

* Remove buildSync command
* feat(inference): Partially implement object shorthand support
* Refs #649
* Use Flow annotations to  enforce types
* Keep flow but switch to comment syntax
* Clarify types
* More flow improvements
* Turn server into class
* LinkerStack becomes class too
* Fix comment description type
* Run flow on lint
* Many more flow fixes
* More intense flow refactoring
* Simplify inference steps
* Update inference tests, flow errors down to 1
* Continue refining types
* Fix more flow issues
* Use 'use strict' everywhere
* Make 'ast' property configurable
* Fix many tests
* Fix more tests
* Fix more tests
* Fix augments
* Test Markdown meta support
* Improve test coverage
* Switch back from for of to for for speed
2017-01-27 16:14:19 -05:00

117 lines
3.3 KiB
JavaScript

/* @flow */
'use strict';
var globalsDocs = require('globals-docs');
var walk = require('../../walk');
/**
* Generate a linker method that links given hardcoded namepaths to URLs
*
* @param {Object} paths an object specified in documentation.yml of hard paths
* @returns {Function} linker
*/
function pathsLinker(paths/* Object */) {
return function (namespace) {
if (paths[namespace]) {
return paths[namespace];
}
};
}
/**
* Given an array of functions, call each of them with `input`
* and return the output of the first one that returns a truthy value.
*
* @param {Array<Function>} fns array of methods
* @param {*} input any input
* @returns {*} any output
*/
function firstPass(fns/*: Array<Function> */, input) {
for (var i = 0; i < fns.length; i++) {
var output = fns[i](input);
if (output) {
return output;
}
}
}
/**
* Create a linking method that takes a namepath and returns a URI
* or a falsy value
*
* @param {Object} config - configuration value
* @returns {Function} linker method
*/
class LinkerStack {
/* :: stack: Array<Function> */
/* :: link: Function */
constructor(config/*: DocumentationConfig */) {
this.stack = [];
if (config.defaultGlobals !== false) {
this.stack.unshift(namespace => {
if (namespace) {
return globalsDocs.getDoc(namespace, config.defaultGlobalsEnvs);
}
});
}
if (config.paths) {
this.stack.unshift(pathsLinker(config.paths));
}
this.link = this._link.bind(this);
}
/**
* Given that the linker stack is a stack of functions, each of which might
* be able to resolve the URL target of a given namespace, namespaceResolver
* adds a function to the stack. You give it a list of comments and it
* adds a function that, if it matches a namespace to a comment, runs
* `resolver` on that comment's namespace in order to get a URL. This makes
* it possible for themes to put each function on a separate page, or at
* different anchors on the same page, and the resolver does stuff like
* adding '#' in front of the namespace or turning the namespace into a URL
* path.
*
* @param {Array<Object>} comments a list of comments
* @param {Function} resolver a method that turns a namespace into a URL
* @returns {LinkerStack} returns this
* @private
* @example
* var linkerStack = new LinkerStack(options)
* .namespaceResolver(comments, function (namespace) {
* var slugger = new GithubSlugger();
* return '#' + slugger.slug(namespace);
* });
*/
namespaceResolver(comments /*: Array<Comment> */, resolver/*: Function */) {
var namespaces = {};
walk(comments, comment => {
namespaces[comment.namespace] = true;
});
this.stack.unshift(namespace => {
if (namespaces[namespace] === true) {
return resolver(namespace);
}
});
return this;
}
/**
* Now that you've configured the LinkerStack with `namespaceResolver`
* and a configuration, run it against a namepath. Might return a URL if
* it can resolve a target, otherwise returns undefined.
*
* @param {string} namepath the namepath of a comment
* @returns {string?} URL target or maybe undefined
* @private
*/
_link(namepath/*: string */) {
return firstPass(this.stack, namepath);
}
}
module.exports = LinkerStack;