<!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>