blot
Version:
The DRY documentation builder
409 lines (325 loc) • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.log = exports.Document = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _apib = require('./apib');
var _env = require('./env');
var env = _interopRequireWildcard(_env);
var _io = require('./io');
var io = _interopRequireWildcard(_io);
var _hazy = require('hazy');
var _hazy2 = _interopRequireDefault(_hazy);
var _cheerio = require('cheerio');
var _cheerio2 = _interopRequireDefault(_cheerio);
var _aglio = require('aglio');
var _aglio2 = _interopRequireDefault(_aglio);
var _log = require('./log');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Represents a collection of HTML DOM elements.
* Contains functionality for working with API Blueprints
* and their view representations.
*/
var Document = exports.Document = function () {
/**
* @param {String} html encoded HTML string
*/
function Document(html) {
_classCallCheck(this, Document);
this.html = html;
}
/**
* Provides project configuration options for HTML views
*
* @returns {Object}
*/
_createClass(Document, [{
key: 'dest',
/**
* Writes out compiled HTML to a filepath (UTF-8)
*
* @param {?String} filepath
* @param {?String} html
* @returns {Promise}
*/
value: function dest(filepath, html) {
return Document.dest(filepath || this.config.dest, html || this.html);
}
/**
* Provides environment configuration options for HTML views
*
* @returns {Object}
*/
}, {
key: 'config',
get: function get() {
return Document.config();
}
/**
* Returns a jQuery-like version of internal HTML
*
* @returns {$}
*/
}, {
key: 'query',
get: function get() {
return Document.query(this.html);
}
/**
* Returns the container object of a template
* if one is configured in environment/project
*
* @returns {$}
*/
}, {
key: 'container',
get: function get() {
return Document.container(this.html);
}
/**
* Strips out elements from HTML based on
* environment/project configuration
*
* @returns {$}
*/
}, {
key: 'stripped',
get: function get() {
return Document.stripped(this.html);
}
/**
* Processes all element configuration filters against HTML
* and returns the resulting document
*
* @returns {$}
*/
}, {
key: 'processed',
get: function get() {
return Document.process(this.html);
}
}], [{
key: 'config',
value: function config() {
return env.current().view;
}
/**
* Determines element selector from environment/project configuration key
*
* @param {?String} configKey top-level key of "views.element" in config file
* @returns {Object}
*/
}, {
key: 'elementConfig',
value: function elementConfig(configKey) {
var config = Document.config().elements[configKey];
if (config instanceof Array && config.length) {
return config.length > 1 ? config.join(', ').trim() : config[0];
}
return config;
}
/**
* Returns a jQuery-like version of internal HTML
*
* @param {String} html
* @returns {$}
*/
}, {
key: 'query',
value: function query(html) {
return _cheerio2.default.load(html);
}
/**
* Returns the container object of a template
* if one is configured in environment/project
*
* @param {String} html
* @returns {$}
*/
}, {
key: 'container',
value: function container(html) {
log('$').info('extracting main container element');
var selector = Document.elementConfig('container');
if (html && selector) {
return Document.query(html)(selector);
}
return Document.query(html);
}
/**
* Plucks out and reduces elements from HTML based on
* environment/project configuration
*
* @param {String} html
* @returns {$}
*/
}, {
key: 'pluck',
value: function pluck(html) {
log('$').info('plucking configured elements');
var selector = Document.elementConfig('pluck');
var query = Document.query;
if (html && selector) {
return query(selector.split(',').map(function ($) {
return query(html)($).map(function (i, elem) {
return query(elem).html();
}).get();
}).reduce(function (a, b) {
return a + b;
}));
} else {
return query(html);
}
}
/**
* Strips out (removes) elements from HTML based on
* environment/project configuration
*
* @param {String} html
* @returns {$}
*/
}, {
key: 'strip',
value: function strip(html) {
log('$').info('stripping configured elements');
if (html) {
var selector = Document.elementConfig('strip');
var query = Document.query(html);
if (selector) query(selector).remove();
return query;
} else {
return Document.query(html);
}
}
/**
* Performs templated regex replacements on HTML based on
* environment/project configuration.
*
* Matches may be dynamically referenced in templates
* by referencing the interpolation token |=$match|
*
* @param {String} html
* @returns {String} resulting HTML
*/
}, {
key: 'replace',
value: function replace(html) {
log('$').info('replacing configured elements');
if (html) {
var replace = Document.config().replace;
var result = html;
if (replace instanceof Array) {
replace.forEach(function (conf) {
var pattern = new RegExp(conf.match, 'gi');
var template = conf.template;
if (pattern && template) {
result = result.replace(pattern, function ($match) {
for (var _len = arguments.length, $sub = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
$sub[_key - 1] = arguments[_key];
}
return _hazy2.default.lang.evaluate(template, { $match: $match, $sub: $sub });
});
} else {
log('$').warn('malformed element replacement configuration', conf);
}
});
}
return result; //Document.query(result) // FIXME - cheerio is escaping only replacements, wut?
}
return html; // Document.query(html)
}
/**
* Processes all element configuration filters against HTML
* and returns the resulting HTML string
*
* @param {String} html
* @returns {String}
*/
}, {
key: 'process',
value: function process(html) {
log('$').info('processing HTML elements');
if (Document.config().elements) {
var containerDom = Document.container(html);
var pluckedDom = Document.pluck(containerDom.html());
var strippedDom = Document.strip(pluckedDom.html());
var bakedHtml = Document.replace(strippedDom.html());
return bakedHtml;
}
return html;
}
/**
* Writes out compiled HTML to a filepath (UTF-8)
*
* @param {String} filepath
* @param {String} html
* @returns {Promise}
*/
}, {
key: 'dest',
value: function dest(filepath, html) {
log('$').info('writing out HTML');
return new Promise(function (resolve, reject) {
if (filepath) {
io.util.fs.dest(filepath, html).then(resolve).catch(reject);
} else {
reject('HTML filepath required');
}
});
}
/**
* Converts a collection of compiled Blueprints
* into HTML via Aglio
*
* @param {Array<Blueprint>} blueprints
* @returns {Promise}
*/
}, {
key: 'fromBlueprints',
value: function fromBlueprints(blueprints) {
return Promise.all(blueprints.map(Document.fromBlueprint));
}
/**
* Converts a compiled Blueprint into HTML via Aglio
*
* @param {Blueprint} blueprint
* @returns {Promise}
*/
}, {
key: 'fromBlueprint',
value: function fromBlueprint(blueprint) {
return new Promise(function (resolve, reject) {
if (blueprint instanceof _apib.Blueprint && blueprint.compiled) {
log('aglio').info('creating HTML from API blueprint');
var locals = { blot: env.current().name, fixtures: blueprint.compiled.fixtures };
var options = Object.assign({ locals: locals }, Document.config().options);
_aglio2.default.render(blueprint.compiled.markdown, options, function (err, html, warnings) {
if (warnings && warnings.length) {
log('aglio').warn('aglio warned: ' + warnings);
}
if (!err) {
log('aglio').info('parsed HTML');
resolve(new Document(html));
} else {
log('aglio').error('aglio errored: ' + err);
reject(err);
}
});
} else {
reject('compiled API blueprint required');
}
});
}
}]);
return Document;
}();
/**
* Module-level bunyan logger
*/
var log = exports.log = function log(sub) {
return (0, _log.logger)().child({ module: sub ? 'html.' + sub : 'html' });
};