<!DOCTYPE html> <meta charset="utf-8"> <style> .node circle { fill: #999; } .node text { font: 10px sans-serif; } .node--internal circle { fill: #555; } .node--internal text { text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff; } .link { fill: none; stroke: #555; stroke-opacity: 0.4; stroke-width: 1.5px; } </style> <svg width="960" height="512"></svg> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.7.1/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/4.0.11/acorn.min.js"></script> <script> fetch("../apps/loader/js/main.js") .then(response => response.text()) .then(text => { const ast = acorn.parse(text, { sourceType: "module", // collect ranges for each node ranges: true, // collect comments in Esprima's format onComment: null, // collect token ranges onToken: null }) console.info(ast) drawAst(ast) }) /* * */ function drawAst(ast) { // Create SVG element //--------------------------- let svg = d3.select("svg") const width = svg.attr("width") const height = svg.attr("height") let g = svg.append("g").attr("transform", "translate(40,0)") // Convert data //--------------------------- const data = acornToHierarchy(ast) // Create D3 Hierarchy //--------------------------- let root = d3.hierarchy(data).sort((a, b) => { return (a.height - b.height) || a.data.name.localeCompare(b.data.name); }) // Create D3 Cluster //--------------------------- let tree = d3.cluster().size([height, width - 200]) tree(root) // Create SVG elements //--------------------------- let link = g.selectAll(".link") .data(root.descendants().slice(1)) .enter().append("path") .attr("class", "link") .attr("d", d => { return `M${d.y},${d.x}C${d.parent.y + 100},${d.x} ${d.parent.y + 100},${d.parent.x} ${d.parent.y},${d.parent.x}` }) let node = g.selectAll(".node") .data(root.descendants()) .enter().append("g") .attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf")) .attr("transform", d => `translate(${d.y},${d.x})`) node.append("circle") .attr("r", 5) node.append("text") .attr("dy", 3) .attr("x", d => d.children ? -8 : 8) .style("text-anchor", d => d.children ? "end" : "start") .text(d => d.data.name) } /* * */ function acornToHierarchy(ast) { console.info(JSON.stringify(ast)) let data = {} for (const clazz of ast.body) { if (clazz.type === "ClassDeclaration") { data.name = clazz.id.name data.children = [] for (const method of clazz.body.body) { if (method.type === "MethodDefinition") { data.children.push({ name: method.key.name, children: [] }) } } } } /* const data = { "name": "Eve", "children": [{ "name": "Cain" }, { "name": "Seth", "children": [{ "name": "Enos" }, { "name": "Noam" }] }, { "name": "Abel" }, { "name": "Awan", "children": [{ "name": "Enoch" }] }, { "name": "Azura" }] } */ return data } </script>