diff --git a/lib/cmds/graph.js b/lib/cmds/graph.js deleted file mode 100644 index ba45743a..00000000 --- a/lib/cmds/graph.js +++ /dev/null @@ -1,118 +0,0 @@ -const Viz = require('viz.js'); -const fs = require('fs'); - -class GraphGenerator { - constructor(engine) { - this.engine = engine; - } - - generate(options) { - let id = 0; - let contractString = ""; - let relationshipString = ""; - let idMapping = {}; - let contractInheritance = {}; - - for (let contract in this.engine.contractsManager.contracts) { - if(options.skipUndeployed && !this.engine.contractsManager.contracts[contract].deploy) continue; - - id++; - - idMapping[contract] = id; - - let contractLabel = ""; - - contractLabel += `${contract}`; - let tooltip = contract; - - if(this.engine.contractsManager.contracts[contract].instanceOf !== undefined && - this.engine.contractsManager.contracts[this.engine.contractsManager.contracts[contract].instanceOf] !== undefined){ - contractInheritance[contract] = this.engine.contractsManager.contracts[contract].instanceOf; - contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf; - tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf; - } else { - if(!(options.skipFunctions === true && options.skipEvents === true)) contractLabel += "|"; - - for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){ - let abiDef = this.engine.contractsManager.contracts[contract].abiDefinition[i]; - if(abiDef.type == 'event' && options.skipEvents) continue; - if(['constructor', 'fallback'].indexOf(abiDef.type) > -1 && options.skipFunctions) continue; - - switch(abiDef.type){ - case 'fallback': - contractLabel += "«fallback»()\\l"; - break; - case 'constructor': - contractLabel += "«constructor»("; - abiDef.inputs.forEach(function(elem, index){ - contractLabel += (index == 0 ? "" : ", ") + elem.type; - }); - contractLabel += ")\\l"; - break; - case 'event': - contractLabel += "«event»" + abiDef.name + "("; - abiDef.inputs.forEach(function(elem, index){ - contractLabel += (index == 0 ? "" : ", ") + elem.type; - }); - contractLabel += ")\\l"; - break; - default: break; - } - } - - let fHashes = this.engine.contractsManager.contracts[contract].functionHashes; - if(fHashes != {} && fHashes != undefined && !options.skipFunctions){ - for(let method in this.engine.contractsManager.contracts[contract].functionHashes){ - contractLabel += method + '\\l'; - } - } - } - - let others = ''; - if(!this.engine.contractsManager.contracts[contract].deploy){ - others = 'fontcolor="#c3c3c3", color="#a0a0a0"'; - tooltip += " (not deployed)"; - } - - contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`; - - } - - for (let c in this.engine.contractsManager.contractDependencies){ - let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c])); - contractDependencies.forEach((d) => { - if(idMapping[c] !== undefined && idMapping[d] !== undefined){ - if((options.skipUndeployed && this.engine.contractsManager.contracts[c].deploy && this.engine.contractsManager.contracts[d].deploy) || !options.skipUndeployed){ - relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`; - } - } - }); - } - - for (let c in contractInheritance){ - if(options.skipUndeployed && !this.engine.contractsManager.contracts[contractInheritance[c]].deploy) continue; - - relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`; - } - - let dot = ` - digraph Contracts { - node[shape=record,style=filled] - edge[dir=back, arrowtail=empty] - ${contractString} - ${relationshipString} - }`; - - let svg = Viz(dot); - - let filename = "diagram.svg"; - - fs.writeFileSync(filename, svg, (err) => { - if (err) throw err; - }); - - - } -} - -module.exports = GraphGenerator; diff --git a/lib/core/engine.js b/lib/core/engine.js index 4aa22b2e..d4bbbe1a 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -82,7 +82,8 @@ class Engine { "web3": this.web3Service, "libraryManager": this.libraryManagerService, "processManager": this.processManagerService, - "storage": this.storageService + "storage": this.storageService, + "graph": this.graphService }; let service = services[serviceName]; @@ -105,6 +106,10 @@ class Engine { }); } + graphService(_options) { + this.registerModule('graph', {engine: this}); + } + pipelineService(_options) { const self = this; this.events.emit("status", "Building Assets"); diff --git a/lib/index.js b/lib/index.js index 24b84b25..b65d35e5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -329,6 +329,7 @@ class Embark { engine.startService("deployment", {onlyCompile: true}); engine.startService("web3"); engine.startService("codeGenerator"); + engine.startService("graph"); engine.events.request('deploy:contracts', callback); } @@ -338,11 +339,9 @@ class Embark { engine.logger.info(err.stack); } else { - const GraphGenerator = require('./cmds/graph.js'); - let graphGen = new GraphGenerator(engine); - graphGen.generate(options); - - engine.logger.info(__("Done. %s generated", "./diagram.svg").underline); + engine.events.request("graph:create", options, () => { + engine.logger.info(__("Done. %s generated", "./diagram.svg").underline); + }); } process.exit(); }); diff --git a/lib/modules/graph/index.js b/lib/modules/graph/index.js new file mode 100644 index 00000000..e99272d2 --- /dev/null +++ b/lib/modules/graph/index.js @@ -0,0 +1,126 @@ +const Viz = require('viz.js'); +const fs = require('fs'); + +class GraphGenerator { + constructor(embark, options) { + const self = this; + this.events = embark.events; + // TODO: this is a very bad dependency to have here, needs to be refactored to use events, etc.. + this.engine = options.engine; + + this.events.setCommandHandler("graph:create", function(options, cb) { + self.generate(options); + cb(); + }); + } + + generate(options) { + let id = 0; + let contractString = ""; + let relationshipString = ""; + let idMapping = {}; + let contractInheritance = {}; + + for (let contract in this.engine.contractsManager.contracts) { + if(options.skipUndeployed && !this.engine.contractsManager.contracts[contract].deploy) continue; + + id++; + + idMapping[contract] = id; + + let contractLabel = ""; + + contractLabel += `${contract}`; + let tooltip = contract; + + if(this.engine.contractsManager.contracts[contract].instanceOf !== undefined && + this.engine.contractsManager.contracts[this.engine.contractsManager.contracts[contract].instanceOf] !== undefined){ + contractInheritance[contract] = this.engine.contractsManager.contracts[contract].instanceOf; + contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf; + tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf; + } else { + if(!(options.skipFunctions === true && options.skipEvents === true)) contractLabel += "|"; + + for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){ + let abiDef = this.engine.contractsManager.contracts[contract].abiDefinition[i]; + if(abiDef.type == 'event' && options.skipEvents) continue; + if(['constructor', 'fallback'].indexOf(abiDef.type) > -1 && options.skipFunctions) continue; + + switch(abiDef.type){ + case 'fallback': + contractLabel += "«fallback»()\\l"; + break; + case 'constructor': + contractLabel += "«constructor»("; + abiDef.inputs.forEach(function(elem, index){ + contractLabel += (index == 0 ? "" : ", ") + elem.type; + }); + contractLabel += ")\\l"; + break; + case 'event': + contractLabel += "«event»" + abiDef.name + "("; + abiDef.inputs.forEach(function(elem, index){ + contractLabel += (index == 0 ? "" : ", ") + elem.type; + }); + contractLabel += ")\\l"; + break; + default: break; + } + } + + let fHashes = this.engine.contractsManager.contracts[contract].functionHashes; + if(fHashes != {} && fHashes != undefined && !options.skipFunctions){ + for(let method in this.engine.contractsManager.contracts[contract].functionHashes){ + contractLabel += method + '\\l'; + } + } + } + + let others = ''; + if(!this.engine.contractsManager.contracts[contract].deploy){ + others = 'fontcolor="#c3c3c3", color="#a0a0a0"'; + tooltip += " (not deployed)"; + } + + contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`; + + } + + for (let c in this.engine.contractsManager.contractDependencies){ + let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c])); + contractDependencies.forEach((d) => { + if(idMapping[c] !== undefined && idMapping[d] !== undefined){ + if((options.skipUndeployed && this.engine.contractsManager.contracts[c].deploy && this.engine.contractsManager.contracts[d].deploy) || !options.skipUndeployed){ + relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`; + } + } + }); + } + + for (let c in contractInheritance){ + if(options.skipUndeployed && !this.engine.contractsManager.contracts[contractInheritance[c]].deploy) continue; + + relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`; + } + + let dot = ` + digraph Contracts { + node[shape=record,style=filled] + edge[dir=back, arrowtail=empty] + ${contractString} + ${relationshipString} + }`; + + let svg = Viz(dot); + + let filename = "diagram.svg"; + + fs.writeFileSync(filename, svg, (err) => { + if (err) throw err; + }); + + + } +} + +module.exports = GraphGenerator;