UNPKG

dependency-cruiser

Version:

Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.

128 lines (116 loc) 3.86 kB
const path = require("node:path"); const figures = require("figures"); const TEMPLATE = ` <html> <head> <style> body { margin: 0; } </style> <script type="text/javascript" src="https://unpkg.com/three"></script> <script type="text/javascript" src="https://unpkg.com/three-spritetext"></script> <script type="text/javascript" src="https://unpkg.com/3d-force-graph"></script> </head> <body> <div id="3d-graph"></div> <script> const gData = { nodes: @@NODES@@, links: @@LINKS@@ }; const elem = document.getElementById('3d-graph') const Graph = ForceGraph3D() (elem) .graphData(gData) .nodeAutoColorBy('group') .nodeLabel(node => node.label) .onNodeHover(node => elem.style.cursor = node ? 'pointer' : null) .onNodeClick(node => { // Aim at node from outside it const distance = 40; const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z); Graph.cameraPosition( { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position node, // lookAt ({ x, y, z }) 3000 // ms transition duration ) }) /* nice idea, but the 3D graph tends to look cluttered. Also GPU/ CPU intensive when run on a serious code base (e.g. react that has ~4500 nodes and ~10000 links */ .nodeThreeObject(node => { const sprite = new SpriteText(node.displayname); sprite.material.depthWrite = true; // make sprite background transparent sprite.color = node.color; sprite.textHeight = 6; return sprite; }) .linkOpacity(0.2) .linkWidth(2) .linkDirectionalArrowLength(4) .linkDirectionalParticles(7) // cool but a bit GPU intensive .linkLabel(link => link.label) .onLinkHover(link => elem.style.cursor = link ? 'pointer' : null) </script> </body> </html>`; function deriveGroup(pFileName) { let lReturnValue = "unknown"; const lGroupPositionInRe = 2; const lMatch = path.dirname(pFileName).match(/^([^/]+)\/([^/]+)/); if (lMatch) { // eslint-disable-next-line security/detect-object-injection lReturnValue = lMatch[lGroupPositionInRe]; } return lReturnValue; } function formatFileName(pFileName) { return `${path.dirname(pFileName)}/<b>${path.basename(pFileName)}</b>`; } function formatDependency(pFrom, pTo) { return `${formatFileName(pFrom)} ${figures.arrowRight}</br>${formatFileName( pTo )}`; } /** * * @param {import('../..').ICruiseResult} pCruiseResult */ function render3DThing(pCruiseResult) { const lNodes = pCruiseResult.modules.map((pModule) => { return { id: pModule.source, displayname: path.basename(pModule.source), dirname: path.dirname(pModule.source), label: formatFileName(pModule.source), group: deriveGroup(pModule.source), }; }); const lLinks = pCruiseResult.modules.reduce( (pAll, pCurrentModule) => pAll.concat( pCurrentModule.dependencies.map((pDependency) => ({ source: pCurrentModule.source, target: pDependency.resolved, label: formatDependency(pCurrentModule.source, pDependency.resolved), })) ), [] ); return TEMPLATE.replace(/@@NODES@@/g, JSON.stringify(lNodes)).replace( /@@LINKS@@/g, JSON.stringify(lLinks) ); } /** * Sample plugin: 3d representation * * @param {import('../..').ICruiseResult} pCruiseResult - * the output of a dependency-cruise adhering to dependency-cruiser's * cruise result schema * @return {import('../..').IReporterOutput} - * output: a string * exitCode: 0 */ module.exports = (pCruiseResult) => ({ output: render3DThing(pCruiseResult), exitCode: 0, });