UNPKG

api-blueprint-visitors

Version:

A Visitor base class that lets you derive your own visitors for manipulating API-Blueprint's AST, so that you can build awesome tools.

135 lines (108 loc) 3.83 kB
/** * Create an accept function for the given node. * @private * @param {string} type * @param {object} node * @param {Array<string>} childrenName */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); 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; }; })(); exports.makeASTVisitable = makeASTVisitable; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _createAccept(type, node, childrenName) { return function (visitor, ctx) { ctx = JSON.parse(JSON.stringify(ctx)); ctx && (ctx[type.toLowerCase()] = node); this.nodeType = type; visitor.visit(this, ctx); childrenName && childrenName.forEach((function (childName) { this[childName] && this[childName].forEach(function (c) { c.accept(visitor, ctx); }); }).bind(this)); }; } /** * Make protagonist's AST visitable, so that it can be traversed with * our visitors. This function decorates the ast with `accept` methods. * @param {object} ast - Protagnist AST */ function makeASTVisitable(ast) { ast.accept = function (visitor) { return ast.resourceGroups.accept(visitor); }; ast.resourceGroups.accept = function (visitor) { this.nodeType = 'Root'; visitor.visit(this); ast.resourceGroups.forEach(function (group) { group.accept(visitor); }); visitor.postVisit(); }; ast.resourceGroups.forEach(function (group) { group.accept = function (visitor) { var ctx = { group: group.name }; this.nodeType = 'Group'; visitor.visit(this, ctx); this.resources && this.resources.forEach(function (r) { r.accept(visitor, ctx); }); }; group.resources.forEach(function (resource) { resource.accept = _createAccept('Resource', resource, ['actions']); resource.actions.forEach(function (action) { action.accept = _createAccept('Action', action, ['examples']); action.examples.forEach(function (example) { example.accept = _createAccept('Example', example, ['requests', 'responses']); example.requests.forEach(function (r) { r.accept = _createAccept('Request', r); }); example.responses.forEach(function (r) { r.accept = _createAccept('Response', r); }); }); }); }); }); } /** * A Visitor base class that knows how to visit ASTs decorated with `makeASTVisitable`. * This class is meant to be extended. * @class */ var Visitor = (function () { function Visitor() { _classCallCheck(this, Visitor); } _createClass(Visitor, [{ key: 'visit', /** * Call your visitor's methods. * This is where the double-dispatch takes place. For instance, if your visitor * implements a `visitResource` method, then this method will call it when a * resource is being visited. * @param {object} node - The node being visited * @param {object} ctx - The parent nodes */ value: function visit(node, ctx) { try { this['visit' + node.nodeType](node, ctx); } catch (e) { // Do nothing if the method isn't implemented } } /** * Post-traversal hook. Will be called when all nodes have been visited. */ }, { key: 'postVisit', value: function postVisit() { // Override in subclasses } }]); return Visitor; })(); exports.Visitor = Visitor;