diff --git a/cmd/cmd_controller.js b/cmd/cmd_controller.js index 995bdf61..d343bae2 100644 --- a/cmd/cmd_controller.js +++ b/cmd/cmd_controller.js @@ -65,6 +65,7 @@ class EmbarkController { let self = this; self.context = options.context || [constants.contexts.run, constants.contexts.build]; let Dashboard = require('./dashboard/dashboard.js'); + let REPL = require('./dashboard/repl.js'); let webServerConfig = { enabled: options.runWebserver @@ -101,6 +102,13 @@ class EmbarkController { async.parallel([ function startDashboard(callback) { if (!options.useDashboard) { + new REPL({ + env: engine.env, + plugins: engine.plugins, + version: engine.version, + events: engine.events, + ipc: engine.ipc + }).startConsole(); return callback(); } @@ -109,7 +117,8 @@ class EmbarkController { logger: engine.logger, plugins: engine.plugins, version: self.version, - env: engine.env + env: engine.env, + ipc: engine.ipc }); dashboard.start(function () { engine.logger.info(__('dashboard start')); @@ -243,7 +252,7 @@ class EmbarkController { console(options) { this.context = options.context || [constants.contexts.run, constants.contexts.console]; - const REPL = require('../lib/dashboard/repl.js'); + const REPL = require('./dashboard/repl.js'); const Engine = require('../lib/core/engine.js'); const engine = new Engine({ env: options.env, @@ -253,7 +262,8 @@ class EmbarkController { embarkConfig: options.embarkConfig || 'embark.json', logFile: options.logFile, logLevel: options.logLevel, - context: this.context + context: this.context, + ipcRole: 'client' }); engine.init(); async.waterfall([ @@ -263,25 +273,58 @@ class EmbarkController { engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); } - engine.startService("processManager"); - engine.startService("serviceMonitor"); - engine.startService("libraryManager"); - engine.startService("codeRunner"); - engine.startService("web3"); - engine.startService("pipeline"); - engine.startService("deployment", {onlyCompile: false}); - engine.startService("storage"); - engine.startService("codeGenerator"); - engine.startService("fileWatcher"); + engine.ipc.connect((err) => { + if (err) { + engine.startService("processManager"); + engine.startService("serviceMonitor"); + engine.startService("libraryManager"); + engine.startService("codeRunner"); + engine.startService("web3"); + engine.startService("pipeline"); + engine.startService("deployment"); + engine.startService("storage"); + engine.startService("codeGenerator"); + engine.startService("webServer"); - callback(); + return callback(); + } + + engine.startService("codeRunner"); + callback(); + }); + }, + function web3IPC(callback) { + if(!engine.ipc.connected || engine.ipc.isServer()) { + return callback(); + } + const Web3 = require('web3'); + let web3 = new Web3(); + engine.ipc.request("runcode:getCommands", null, (_, {web3Config, commands}) => { + web3.setProvider(web3Config.provider.host); + web3.eth.defaultAccount = web3Config.defaultAccount; + engine.events.emit("runcode:register", "web3", web3); + async.each(commands, ({varName, code}, next) => { + if (varName) { + engine.events.emit("runcode:register", varName, code); + } else { + engine.events.request("runcode:eval", code); + } + next(); + }, callback); + }); }, function deploy(callback) { + if(engine.ipc.connected && engine.ipc.isClient()) { + return callback(); + } engine.events.request('deploy:contracts', function (err) { callback(err); }); }, function waitForWriteFinish(callback) { + if(engine.ipc.connected && engine.ipc.isClient()) { + return callback(); + } engine.logger.info("Finished deploying".underline); engine.events.once('outputDone', (err) => { engine.logger.info(__("finished building").underline); @@ -293,7 +336,8 @@ class EmbarkController { env: engine.env, plugins: engine.plugins, version: engine.version, - events: engine.events + events: engine.events, + ipc: engine.ipc }); repl.start(callback); } diff --git a/cmd/dashboard/console.js b/cmd/dashboard/console.js index 805ef965..1c23cfd8 100644 --- a/cmd/dashboard/console.js +++ b/cmd/dashboard/console.js @@ -5,11 +5,11 @@ class Console { this.events = options.events; this.plugins = options.plugins; this.version = options.version; - this.contractsConfig = options.contractsConfig; - } + this.ipc = options.ipc; - runCode(code) { - this.events.request('runcode:eval', code); + if (this.ipc.isServer()) { + this.ipc.on('console:executeCmd', this.executeCmd.bind(this)); + } } processEmbarkCmd (cmd) { @@ -37,26 +37,25 @@ class Console { executeCmd(cmd, callback) { var pluginCmds = this.plugins.getPluginsProperty('console', 'console'); for (let pluginCmd of pluginCmds) { - let pluginOutput = pluginCmd.call(this, cmd, {}); - if (pluginOutput !== false && pluginOutput !== 'false' && pluginOutput !== undefined) return callback(pluginOutput); + let pluginResult = pluginCmd.call(this, cmd, {}); + if (pluginResult.match()) { + return pluginResult.process(callback); + } } let output = this.processEmbarkCmd(cmd); if (output) { - return callback(output); + return callback(null, output); } try { - this.events.request('runcode:eval', cmd, (err, result) => { - callback(result); - }); + this.events.request('runcode:eval', cmd, callback, true); } catch (e) { - if (e.message.indexOf('not defined') > 0) { - return callback(("error: " + e.message).red + ("\n" + __("Type") + " " + "help".bold + " " + __("to see the list of available commands")).cyan); - } else { - return callback(e.message); + if (this.ipc.connected && this.ipc.isClient()) { + return this.ipc.request('console:executeCmd', cmd, callback); } + return callback(e.message); } } } diff --git a/cmd/dashboard/dashboard.js b/cmd/dashboard/dashboard.js index 0bf7a7a5..d1727f7d 100644 --- a/cmd/dashboard/dashboard.js +++ b/cmd/dashboard/dashboard.js @@ -11,6 +11,7 @@ class Dashboard { this.plugins = options.plugins; this.version = options.version; this.env = options.env; + this.ipc = options.ipc; this.events.on('firstDeploymentDone', this.checkWindowSize.bind(this)); this.events.on('outputDone', this.checkWindowSize.bind(this)); @@ -32,7 +33,8 @@ class Dashboard { console = new Console({ events: self.events, plugins: self.plugins, - version: self.version + version: self.version, + ipc: self.ipc }); callback(); }, diff --git a/cmd/dashboard/monitor.js b/cmd/dashboard/monitor.js index c437599d..b758a2ec 100644 --- a/cmd/dashboard/monitor.js +++ b/cmd/dashboard/monitor.js @@ -368,7 +368,7 @@ class Monitor { executeCmd(cmd, cb) { const self = this; self.logText.log('console> '.bold.green + cmd); - self.console.executeCmd(cmd, function (result) { + self.console.executeCmd(cmd, function (_, result) { self.logText.log(result); if (cb) { cb(result); diff --git a/cmd/dashboard/repl.js b/cmd/dashboard/repl.js index bd5442fc..1951c4f1 100644 --- a/cmd/dashboard/repl.js +++ b/cmd/dashboard/repl.js @@ -8,17 +8,21 @@ class REPL { this.env = options.env; this.plugins = options.plugins; this.events = options.events; + this.version = options.version; + this.ipc = options.ipc; + } + + startConsole(){ this.console = new Console({ events: this.events, plugins: this.plugins, - version: options.version + version: this.version, + ipc: this.ipc }); } enhancedEval(cmd, context, filename, callback) { - this.console.executeCmd(cmd.trim(), (result) => { - callback(null, result); - }); + this.console.executeCmd(cmd.trim(), callback); } enhancedWriter(output) { @@ -30,6 +34,7 @@ class REPL { } start(done) { + this.startConsole(); this.replServer = repl.start({ prompt: "Embark (" + this.env + ") > ", useGlobal: true, diff --git a/lib/core/engine.js b/lib/core/engine.js index c401233d..29bf577c 100644 --- a/lib/core/engine.js +++ b/lib/core/engine.js @@ -17,6 +17,7 @@ class Engine { this.context = options.context; this.useDashboard = options.useDashboard; this.webServerConfig = options.webServerConfig; + this.ipcRole = options.ipcRole; } init(_options) { @@ -35,6 +36,11 @@ class Engine { if (this.interceptLogs || this.interceptLogs === undefined) { utils.interceptLogs(console, this.logger); } + + this.ipc = new IPC({logger: this.logger, ipcRole: this.ipcRole}); + if (this.ipc.isServer()) { + this.ipc.serve(); + } } registerModule(moduleName, options) { @@ -126,7 +132,8 @@ class Engine { this.codeRunner = new CodeRunner({ plugins: this.plugins, events: this.events, - logger: this.logger + logger: this.logger, + ipc: this.ipc }); } @@ -157,18 +164,13 @@ class Engine { let self = this; this.registerModule('compiler', {plugins: self.plugins}); - - this.ipc = new IPC({logger: this.logger, ipcRole: options.ipcRole}); - if (this.ipc.isServer()) { - this.ipc.serve(); - } - - this.registerModule('solidity', {ipc: this.ipc, useDashboard: this.useDashboard}); + this.registerModule('solidity', {ipc: self.ipc, useDashboard: this.useDashboard}); this.registerModule('vyper'); this.registerModule('profiler'); this.registerModule('deploytracker'); this.registerModule('specialconfigs'); - this.registerModule('console_listener', {ipc: this.ipc}); + this.registerModule('specialconfigs'); + this.registerModule('console_listener', {ipc: self.ipc}); this.registerModule('contracts_manager'); this.registerModule('deployment', {plugins: this.plugins, onlyCompile: options.onlyCompile}); diff --git a/lib/core/ipc.js b/lib/core/ipc.js index de028afa..5aad37b4 100644 --- a/lib/core/ipc.js +++ b/lib/core/ipc.js @@ -52,7 +52,7 @@ class IPC { return; } let reply = function(_err, replyData) { - self.reply(socket, 'compile', replyData); + self.reply(socket, action, replyData); }; done(data.message, reply, socket); }); @@ -62,6 +62,14 @@ class IPC { ipc.server.emit(client, 'message', {action: action, message: data}); } + listenTo(action, callback) { + ipc.of['embark'].on(action, callback); + } + + broadcast(action, data) { + ipc.server.broadcast(action, data); + } + once(action, cb) { ipc.of['embark'].once('message', function(msg) { if (msg.action !== action) { diff --git a/lib/core/modules/coderunner/codeRunner.js b/lib/core/modules/coderunner/codeRunner.js index a7c3e153..925d7e76 100644 --- a/lib/core/modules/coderunner/codeRunner.js +++ b/lib/core/modules/coderunner/codeRunner.js @@ -6,27 +6,50 @@ class CodeRunner { this.plugins = options.plugins; this.logger = options.logger; this.events = options.events; - + this.ipc = options.ipc; + this.commands = []; + let self = this; // necessary to init the context RunCode.initContext(); + if (this.ipc.isServer()) { + this.ipc.on('runcode:getCommands', (_, callback) => { + let result = {web3Config: RunCode.getWeb3Config(), commands: self.commands}; + callback(null, result); + }); + } + + if (this.ipc.isClient() && this.ipc.connected) { + this.ipc.listenTo('runcode:newCommand', function (command) { + if (command.varName) { + self.events.emit("runcode:register", command.varName, command.code); + } else { + self.events.request("runcode:eval", command.code); + } + }); + } + this.events.on("runcode:register", (varName, code) => { + if (self.ipc.isServer() && varName !== 'web3') { + self.commands.push({varName, code}); + self.ipc.broadcast("runcode:newCommand", {varName, code}); + } RunCode.registerVar(varName, code); }); - this.events.setCommandHandler('runcode:eval', (code, cb) => { + this.events.setCommandHandler('runcode:eval', (code, cb, dashboard = false) => { if (!cb) { cb = function() {}; } - try { - let result = RunCode.doEval(code); - cb(null, result); - } catch (e) { - cb(e); + let result = RunCode.doEval(code); + if (!dashboard && self.ipc.isServer()) { + self.commands.push({code}); + self.ipc.broadcast("runcode:newCommand", {code}); } - + cb(null, result); }); } + } module.exports = CodeRunner; diff --git a/lib/core/modules/coderunner/runCode.js b/lib/core/modules/coderunner/runCode.js index 9560eec1..799a324d 100644 --- a/lib/core/modules/coderunner/runCode.js +++ b/lib/core/modules/coderunner/runCode.js @@ -23,8 +23,13 @@ function registerVar(varName, code) { __mainContext[varName] = code; } +function getWeb3Config() { + return {defaultAccount:__mainContext.web3.eth.defaultAccount, provider: __mainContext.web3.currentProvider}; +} + module.exports = { - doEval: doEval, - registerVar: registerVar, - initContext: initContext + doEval, + registerVar, + initContext, + getWeb3Config }; diff --git a/lib/index.js b/lib/index.js index 03bb5cd9..f8b5c331 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,3 @@ -let async = require('async'); -const constants = require('./constants'); - -require('colors'); - -// Set PWD to CWD since Windows doesn't have a value for PWD -if (!process.env.PWD) { - process.env.PWD = process.cwd(); -} - let version = require('../package.json').version; class Embark { @@ -29,7 +19,6 @@ class Embark { this.config.loadConfigFiles(options); this.plugins = this.config.plugins; } - } module.exports = Embark; diff --git a/lib/modules/library_manager/index.js b/lib/modules/library_manager/index.js index 9e37a62d..032d643e 100644 --- a/lib/modules/library_manager/index.js +++ b/lib/modules/library_manager/index.js @@ -38,14 +38,16 @@ class LibraryManager { registerCommands() { const self = this; this.embark.registerConsoleCommand((cmd, _options) => { - if (cmd === "versions" || cmd === __('versions')) { - let text = [__('versions in use') + ':']; - for (let lib in self.versions) { - text.push(lib + ": " + self.versions[lib]); + return { + match: () => cmd === "versions" || cmd === __('versions'), + process: (callback) => { + let text = [__('versions in use') + ':']; + for (let lib in self.versions) { + text.push(lib + ": " + self.versions[lib]); + } + callback(null, text.join('\n')); } - return text.join('\n'); - } - return false; + }; }); } diff --git a/lib/modules/profiler/index.js b/lib/modules/profiler/index.js index 4a490400..b5e59967 100644 --- a/lib/modules/profiler/index.js +++ b/lib/modules/profiler/index.js @@ -11,7 +11,7 @@ class Profiler { this.registerConsoleCommand(); } - profile(contractName, contract) { + profile(contractName, contract, callback) { const self = this; let table = new asciiTable(contractName); table.setHeading('Function', 'Payable', 'Mutability', 'Inputs', 'Outputs', 'Gas Estimates'); @@ -33,7 +33,7 @@ class Profiler { table.addRow(abiMethod.name, abiMethod.payable, abiMethod.stateMutability, self.formatParams(abiMethod.inputs), self.formatParams(abiMethod.outputs), gastimates[abiMethod.name]); } }); - self.logger.info(table.toString()); + callback(null, table.toString()); }); } @@ -53,18 +53,18 @@ class Profiler { self.embark.registerConsoleCommand((cmd, _options) => { let cmdName = cmd.split(' ')[0]; let contractName = cmd.split(' ')[1]; - if (cmdName === 'profile') { - self.events.request('contracts:contract', contractName, (contract) => { - if (!contract || !contract.deployedAddress) { - self.logger.info("-- couldn't profile " + contractName + " - it's not deployed or could be an interface"); - return ""; - } - self.logger.info("-- profile for " + contractName); - this.profile(contractName, contract); - }); - return ""; - } - return false; + + return { + match: () => cmdName === 'profile', + process: (callback) => { + self.events.request('contracts:contract', contractName, (contract) => { + if (!contract || !contract.deployedAddress) { + return "-- couldn't profile " + contractName + " - it's not deployed or could be an interface"; + } + this.profile(contractName, contract, callback); + }); + } + }; }); } } diff --git a/lib/modules/webserver/index.js b/lib/modules/webserver/index.js index a4dada8a..23383138 100644 --- a/lib/modules/webserver/index.js +++ b/lib/modules/webserver/index.js @@ -50,18 +50,25 @@ class WebServer { registerConsoleCommands() { const self = this; self.embark.registerConsoleCommand((cmd, _options) => { - if (cmd === 'webserver start') { - self.events.request("start-webserver"); - return " "; - } - if (cmd === 'webserver stop') { - self.events.request("stop-webserver"); - return __("stopping webserver") + "..."; - } - return false; + return { + match: () => cmd === "webserver start", + process: (callback) => { + self.events.request("start-webserver"); + callback(null, "OK"); + } + }; + }); + + self.embark.registerConsoleCommand((cmd, _options) => { + return { + match: () => cmd === "webserver stop", + process: (callback) => { + self.events.request("stop-webserver"); + callback(null, "OK"); + } + }; }); } - } module.exports = WebServer; diff --git a/lib/tests/test.js b/lib/tests/test.js index 10ef7405..9753f143 100644 --- a/lib/tests/test.js +++ b/lib/tests/test.js @@ -116,8 +116,7 @@ class Test { web3: this.web3 }); this.engine.startService("deployment", { - trackContracts: false, - ipcRole: 'client' + trackContracts: false }); this.events.request('deploy:setGasLimit', 6000000); this.engine.startService("codeCoverage"); @@ -128,7 +127,8 @@ class Test { env: this.options.env || 'test', // TODO: config will need to detect if this is a obj embarkConfig: this.options.embarkConfig || 'embark.json', - interceptLogs: false + interceptLogs: false, + ipcRole: 'client' }); this.engine.init({ diff --git a/test/console.js b/test/console.js index eca2814e..0ed7eebc 100644 --- a/test/console.js +++ b/test/console.js @@ -1,19 +1,21 @@ /*globals describe, it*/ let Console = require('../cmd/dashboard/console.js'); let Plugins = require('../lib/core/plugins.js'); +let IPC = require('../lib/core/ipc.js'); let assert = require('assert'); let version = require('../package.json').version; describe('embark.Console', function() { + let ipc = new IPC({ipcRole: 'none'}); let plugins = new Plugins({plugins: {}}); - let console = new Console({plugins: plugins, version: version}); + let console = new Console({plugins, version, ipc}); describe('#executeCmd', function() { describe('command: help', function() { it('it should provide a help text', function(done) { - console.executeCmd('help', function(output) { + console.executeCmd('help', function(_err, output) { let lines = output.split('\n'); assert.equal(lines[0], 'Welcome to Embark ' + version); assert.equal(lines[2], 'possible commands are:');