'use strict'; const archy = require("archy"); const chalk = require("chalk"); const memoizee = require("memoizee"); const assureArray = require("assure-array"); const util = require("util"); const colorizedNodeType = require("./render/colorize-type"); const shortDescription = require("./render/short-description"); const sectionTag = require("./render/section-tag"); module.exports = function visualizeAST(ast) { let indexedItems = []; // NOTE: Impure, but is necessary to make getItemAtIndex work... function sectionLabel(name, items) { return { label: chalk.yellow(`${name}:`), nodes: items } } function convertChildNodes(key, childNodes) { let arrayifiedNodes = assureArray(childNodes); let treeNodes = arrayifiedNodes.map((node) => { let treeNode = astNodeToTreeNode(node); treeNode.label = sectionTag(key, {isArray: Array.isArray(childNodes)}) + " " + treeNode.label; return treeNode; }); return treeNodes; } function astNodeToTreeNode(astNode) { indexedItems.push(astNode); let nodeLabel = `${chalk.bold(colorizedNodeType(astNode))} ${shortDescription(astNode)}`; let primitiveProperties = [], singleChildren = [], multipleChildren = []; Object.keys(astNode).forEach((key) => { if (key === "loc" || key === "range") { /* These are Esprima annotations. */ return; } let value = astNode[key]; if (typeof value === "string" || typeof value === "boolean" || typeof value === "number" || value == null) { primitiveProperties.push(key); } else if (Array.isArray(value)) { if (value.length > 0) { multipleChildren.push(key); } } else if (typeof value === "object" && value.type != null) { singleChildren.push(key); } else { throw new Error(`Encountered unrecognized value type in AST: ${typeof value} -- ${util.inspect(value)}`); } }); let childNodes = (singleChildren.concat(multipleChildren)).map((key) => { return convertChildNodes(key, astNode[key]); }).reduce((combined, array) => { return combined.concat(array); }, []); return { label: nodeLabel, nodes: childNodes } } return { getString: memoizee(function getString() { let treeNodes = astNodeToTreeNode(ast); return archy(treeNodes); }), getLines: function getLines() { return this.getString().split("\n"); }, getItemAtIndex: function getItemAtIndex(index) { return indexedItems[index]; } } }