remove code not beloging to code runner & other core modules; disable code generator and move contract code to its own module

cleanup pipeline; start preparing for plugin based pipeline file generation

initial work to write pipeline files through an api

add action events to pipeline; generate contracts json and js files

move old pipeline to its own module; generate basic artifacts

re-add missing plugins function

add basic embarkjs provider registration

refactor embark-whisper; move whisper api; execute whisper code in console

add api to register help cmds in console; start moving hardcoded help cmds out

cleanup embark-graph

add todos
This commit is contained in:
Iuri Matias 2019-07-10 16:29:58 -04:00
parent 783b555dd2
commit 50c3525374
37 changed files with 1041 additions and 691 deletions

View File

@ -20,7 +20,7 @@
"ipfs-api": "17.2.7"
},
"plugins": {
"embark-dapp-test-service": {}
"embarkjs-connector-web3": {}
},
"options": {
"solc": {

View File

@ -4,16 +4,12 @@ import VM from "./vm";
export { fs, VM };
import { Callback, Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark";
import Web3 from "web3";
// TODO: ideally shouldn't be needed or should be done through an API
import Web3 from "web3";
const EmbarkJS = require("embarkjs");
export enum ProviderEventType {
ProviderRegistered = "providerRegistered",
ProviderSet = "providerSet",
}
export default class CodeRunner {
class CodeRunner {
private ready: boolean = false;
private blockchainConnected: boolean = false;
private logger: Logger;
@ -31,6 +27,7 @@ export default class CodeRunner {
fs,
},
},
// TODO: ideally shouldn't be needed or should be done through an API
sandbox: {
EmbarkJS,
Web3,
@ -39,17 +36,7 @@ export default class CodeRunner {
this.registerEvents();
this.registerCommands();
}
private generateListener(provider: string, eventType: ProviderEventType) {
const providerStateName = `${provider}:${eventType}`;
const eventName = `runcode:${providerStateName}`;
this.events.setCommandHandler(eventName, (cb) => {
if (this.providerStates[providerStateName] === true) {
return cb();
}
this.events.once(eventName, cb);
});
this.ready = true;
}
private registerEvents() {
@ -61,44 +48,6 @@ export default class CodeRunner {
cb(this.vm.options.sandbox);
});
this.events.setCommandHandler("runcode:eval", this.evalCode.bind(this));
this.events.setCommandHandler("runcode:embarkjs:reset", (cb) => {
this.resetEmbarkJS(cb);
});
this.events.on("contractsDeployed", () => {
this.events.on("code-generator-ready", (_modifiedAssets: any) => {
this.resetEmbarkJS();
});
});
// register listeners for when EmbarkJS runs registerProvider through the console.
// For example, when `EmbarkJS.Storage.registerProvider(...)` is run through the console,
// emit the `runcode:storage:providerRegistered` event, and fire any requests attached to it
Object.keys(EmbarkJS)
.filter((propName) => EmbarkJS[propName].hasOwnProperty("registerProvider"))
.forEach((providerName) => {
this.generateListener(providerName.toLowerCase(), ProviderEventType.ProviderRegistered);
});
// register listeners for when EmbarkJS runs setProvider through the console.
// For example, when `EmbarkJS.Storage.setProvider(...)` is run through the console,
// emit the `runcode:storage:providerSet` event, and fire any requests attached to it
Object.keys(EmbarkJS)
.filter((propName) => EmbarkJS[propName].hasOwnProperty("setProvider"))
.forEach((providerName) => {
this.generateListener(providerName.toLowerCase(), ProviderEventType.ProviderSet);
});
}
private resetEmbarkJS(cb?: Callback<null>) {
this.events.request("code-generator:embarkjs:init-provider-code", (code: string) => {
this.evalCode(code, (err) => {
if (err) {
console.dir(err);
return;
}
if (cb) { cb(); }
}, false);
});
}
private registerVar(varName: string, code: any, cb = () => { }) {
@ -121,3 +70,5 @@ export default class CodeRunner {
});
}
}
export default CodeRunner;

View File

@ -38,6 +38,7 @@ class VM {
"@babel/runtime-corejs2/core-js/promise",
"@babel/runtime-corejs2/helpers/interopRequireDefault",
"embark-utils",
// TODO: ideally this shouldnt' be needed/here or should be configurable by the modules themselves somehow
"embarkjs-ens",
"embarkjs-ipfs",
"embarkjs-swarm",

View File

@ -28,6 +28,8 @@ class Console {
private history: string[];
private cmdHistoryFile: string;
private suggestions?: Suggestions;
private providerReady: boolean;
private helpCmds: any;
constructor(embark: Embark, options: any) {
this.embark = embark;
@ -41,6 +43,7 @@ class Console {
this.history = [];
this.cmdHistoryFile = options.cmdHistoryFile || dappPath(".embark", "cmd_history");
this.loadHistory();
this.helpCmds = {};
if (this.ipc.isServer()) {
this.ipc.on("console:executeCmd", (cmd: string, cb: any) => {
@ -57,6 +60,15 @@ class Console {
this.saveHistory(cmd, true);
});
}
this.events.setCommandHandler("console:register:helpCmd", (options: any, cb: any) => {
const {cmdName, cmdHelp} = options;
this.helpCmds[cmdName] = cmdHelp;
if (cb) { cb(); }
});
this.events.setCommandHandler("console:unregister:helpCmd", (cmdName: string, cb: any) => {
delete this.helpCmds[cmdName];
if (cb) { cb(); }
});
this.events.setCommandHandler("console:executeCmd", this.executeCmd.bind(this));
this.events.setCommandHandler("console:history", (cb: any) => this.getHistory(this.cmdHistorySize(), cb));
this.registerConsoleCommands();
@ -106,14 +118,20 @@ class Console {
__("Welcome to Embark") + " " + this.version,
"",
__("possible commands are:"),
// TODO: only if the blockchain is actually active!
// will need to pass te current embark state here
// TODO: this help commands should be passed through an API
chalk.cyan("ipfs") + " - " + __("instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)"),
chalk.cyan("swarm") + " - " + __("instantiated swarm-api object configured to the current environment (available if swarm is enabled)"),
chalk.cyan("web3") + " - " + __("instantiated web3.js object configured to the current environment"),
chalk.cyan("EmbarkJS") + " - " + __("EmbarkJS static functions for Storage, Messages, Names, etc."),
chalk.cyan("log [process] on/off") + " - " + __("Activate or deactivate the logs of a sub-process. Options: blockchain, ipfs, webserver"),
];
for (let cmdName in this.helpCmds) {
let helpCmd = this.helpCmds[cmdName];
helpText.push(chalk.cyan(cmdName) + " - " + helpCmd);
}
// TODO: remove old helpDescriptions
helpDescriptions.forEach((helpDescription) => {
let matches = [] as string[];
if (Array.isArray(helpDescription.matches)) {

View File

@ -14,6 +14,13 @@ interface Suggestion {
type SuggestionsList = Suggestion[];
// =============================================
// =============================================
// TODO: this should be moved to its own module
// it's a plugin not a core module
// =============================================
// =============================================
export default class Suggestions {
private embark: Embark;
private events: Events;

View File

@ -62,7 +62,7 @@ export class ProcessLauncher {
return self.exitCallback(code);
}
if (code) {
this.logger.info(`Child Process ${this.name} exited with code ${code}`);
self.logger.info(`Child Process ${this.name} exited with code ${code}`);
}
});
}

View File

@ -23,7 +23,8 @@ export class ProcessManager {
this._registerApiCalls();
this._registerEvents();
this.events.once("deploy:beforeAll", this._registerCommands.bind(this));
// TODO: removed because Process Manager shouldn't care or have knoweldge about deployment
// this.events.once("deploy:beforeAll", this._registerCommands.bind(this));
}
_registerApiCalls() {

View File

@ -34,7 +34,8 @@ class DeployTracker {
if (this.chainFile === false) return;
const self = this;
this.embark.registerActionForEvent("deploy:beforeAll", this.setCurrentChain.bind(this));
// TODO: re-add
// this.embark.registerActionForEvent("deploy:beforeAll", this.setCurrentChain.bind(this));
this.events.on("deploy:contract:deployed", (contract) => {
self.trackContract(contract.className, contract.realRuntimeBytecode, contract.realArgs, contract.deployedAddress);

View File

@ -85,6 +85,7 @@ class ContractDeployer {
}
checkAndDeployContract(contract, params, callback, returnObject) {
console.dir("= checkAndDeployContract: " + contract.className);
let self = this;
contract.error = false;
let accounts = [];
@ -176,6 +177,7 @@ class ContractDeployer {
return self.deployContract(contract, next, returnObject);
}
console.dir("== emitAndRunActionsForEvent / should Deploy");
self.plugins.emitAndRunActionsForEvent('deploy:contract:shouldDeploy', {contract: contract, shouldDeploy: true}, function(_err, params) {
let trackedContract = params.contract;
if (!params.shouldDeploy) {
@ -202,7 +204,9 @@ class ContractDeployer {
});
});
}
], callback);
], (err,results) => {
callback(err, results);
});
}
willNotDeployContract(contract, trackedContract, callback) {
@ -212,26 +216,13 @@ class ContractDeployer {
}
contractAlreadyDeployed(contract, trackedContract, callback) {
console.dir("--> contractAlreadyDeployed")
this.logFunction(contract)(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
contract.deployedAddress = trackedContract.address;
this.events.emit("deploy:contract:deployed", contract);
this.registerContract(contract, callback);
}
registerContract(contract, callback) {
this.events.request('code-generator:contract:custom', contract, (contractCode) => {
this.events.request('runcode:eval', contractCode, (err) => {
if (err) {
return callback(err);
}
this.events.request('runcode:eval', contract.className, (err, result) => {
if (err) {
return callback(err);
}
this.events.emit("runcode:register", contract.className, result, callback);
});
});
this.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, (err) => {
callback(err);
});
}
@ -240,11 +231,13 @@ class ContractDeployer {
}
deployContract(contract, callback, returnObject) {
console.dir("deployContract")
let self = this;
let deployObject;
async.waterfall([
function doLinking(next) {
console.dir("= doLinking")
if (!contract.linkReferences || !Object.keys(contract.linkReferences).length) {
return next();
@ -288,11 +281,13 @@ class ContractDeployer {
});
},
function applyBeforeDeploy(next) {
console.dir("= applyBeforeDeploy")
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, (_params) => {
next();
});
},
function getGasPriceForNetwork(next) {
console.dir("= getGasPriceForNetwork")
self.events.request("blockchain:gasPrice", (err, gasPrice) => {
if (err) {
return next(new Error(__("could not get the gas price")));
@ -302,6 +297,7 @@ class ContractDeployer {
});
},
function createDeployObject(next) {
console.dir("= createDeployObject")
let contractCode = contract.code;
let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});
let contractParams = (contract.realArgs || contract.args).slice();
@ -343,6 +339,7 @@ class ContractDeployer {
});
},
function deployTheContract(next) {
console.dir("= deployTheContract " + contract.className)
let estimatedCost = contract.gas * contract.gasPrice;
self.blockchain.deployContractFromObject(deployObject, {
@ -350,6 +347,9 @@ class ContractDeployer {
gas: contract.gas,
gasPrice: contract.gasPrice
}, function(error, receipt) {
console.dir("--> contract deployed")
console.dir(error)
console.dir(receipt)
if (error) {
contract.error = error.message;
self.events.emit("deploy:contract:error", contract);
@ -366,19 +366,25 @@ class ContractDeployer {
if(receipt) self.events.emit("deploy:contract:receipt", receipt);
self.events.emit("deploy:contract:deployed", contract);
self.registerContract(contract, () => {
// console.dir("__registerContract")
// self.registerContract(contract, () => {
console.dir("__runActionsForEvent deploy:contract:deployed")
self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, (err) => {
console.dir("result __runActionsForEvent deploy:contract:deployed")
if (err) {
console.dir(err)
return next(err);
}
next(null, receipt);
});
});
}, hash => {
self.logFunction(contract)(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas at the price of").green + " " + contract.gasPrice + " " + __("Wei, estimated cost:").green + " " + estimatedCost + " Wei".green + " (txHash: " + hash.bold.cyan + ")");
});
}
], callback);
], (__err, __results) => {
console.dir("--- deployed Contract")
callback(__err, __results)
});
}
}

View File

@ -54,9 +54,12 @@ class DeployManager {
function (next) {
self.logger.info(__('Executing pre-deploy actions...'));
self.plugins.emitAndRunActionsForEvent("deploy:beforeAll", (err) => {
if (err) {
return next(err);
}
// console.dir("== err")
// console.dir(err)
// TODO: err is a function for some reason
// if (err) {
// return next(err);
// }
self.logger.info(__('Pre-deploy actions done. Deploying contracts'));
next();
});
@ -64,6 +67,8 @@ class DeployManager {
function (next) {
const contractDeploys = {};
const errors = [];
console.dir("=== contracts")
console.dir(contracts.map((x) => x.className))
contracts.forEach(contract => {
function deploy(result, callback) {
if (typeof result === 'function') {
@ -71,7 +76,10 @@ class DeployManager {
}
contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, (err) => {
console.dir("contract deployed " + contract.className)
if (err) {
console.dir("== err deploying contract");
console.dir(err);
contract.error = err.message || err;
if (contract.error === constants.blockchain.gasAllowanceError) {
self.logger.error(`[${contract.className}]: ${constants.blockchain.gasAllowanceErrorMessage}`);
@ -93,7 +101,14 @@ class DeployManager {
contractDeploys[className].push(deploy);
});
console.dir("== async.auto");
console.dir(Object.keys(contractDeploys));
console.dir(contractDeploys);
async.auto(contractDeploys, function(_err, _results) {
if (_err) {
console.dir("error deploying contracts")
console.dir(_err)
}
if (errors.length) {
_err = __("Error deploying contracts. Please fix errors to continue.");
self.logger.error(_err);
@ -109,6 +124,7 @@ class DeployManager {
});
}
], (err) => {
console.dir("==== finished deploying")
if (err) {
self.logger.error(err);
}

View File

@ -150,6 +150,12 @@ class ENS {
registerEvents() {
this.embark.registerActionForEvent("deploy:beforeAll", this.configureContractsAndRegister.bind(this));
if (this.eventsRegistered) {
return;
}
this.eventsRegistered = true;
// TODO: re-add
// this.embark.registerActionForEvent("deploy:beforeAll", this.configureContractsAndRegister.bind(this));
this.events.on('blockchain:reseted', this.reset.bind(this));
this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this));
this.events.setCommandHandler("ens:config", this.getEnsConfig.bind(this));

View File

@ -1,21 +1,16 @@
const async = require('async');
const Viz = require('viz.js');
const fs = require('fs');
class GraphGenerator {
constructor(embark, _options) {
const self = this;
this.embark = embark;
this.events = embark.events;
this.contracts = [];
this.events.setCommandHandler("graph:create", function(options, cb) {
self.generate(options);
cb();
});
this.events.setCommandHandler("graph:create", this.generate.bind(this));
}
/*eslint complexity: ["error", 21]*/
generate(options) {
generate(options, cb) {
const self = this;
let id = 0;
let contractString = "";
@ -23,6 +18,7 @@ class GraphGenerator {
let idMapping = {};
let contractInheritance = {};
let contractsDependencies = {};
this.contracts = [];
async.waterfall([
function getContractList(next) {
@ -134,12 +130,13 @@ class GraphGenerator {
let svg = Viz(dot);
fs.writeFileSync(options.output, svg, (err) => {
self.embark.fs.writeFileSync(options.output, svg, (err) => {
if (err) throw err;
next();
});
}
], function(_err, _result) {
cb();
});
}

View File

@ -2,6 +2,8 @@ const ProcessLogsApi = require('embark-process-logs-api');
const EMBARK_PROCESS_NAME = 'embark';
// TODO: looks unnecessary, should be moved to a common module together with embark-process-logs-api
/**
* EmbarkListener has two functions:
* 1. Register API endpoints (HTTP GET and WS) to retrieve embark logs.

View File

@ -53,8 +53,7 @@
"embark-i18n": "^4.1.0-beta.3",
"embark-utils": "^4.1.0-beta.5",
"find-up": "2.1.0",
"fs-extra": "7.0.1",
"webpack": "4.29.3"
"fs-extra": "7.0.1"
},
"devDependencies": {
"@babel/cli": "7.2.3",

View File

@ -0,0 +1,119 @@
const path = require('path');
import { dappPath, fileTreeSort } from 'embark-utils';
class API {
constructor(embark, _options) {
this.plugins = embark.config.plugins;
}
registerAPIs() {
let plugin = this.plugins.createPlugin('deployment', {});
plugin.registerAPICall(
'get',
'/embark-api/file',
(req, res) => {
try {
this.apiGuardBadFile(req.query.path);
} catch (error) {
return res.send({ error: error.message });
}
const name = path.basename(req.query.path);
const content = this.fs.readFileSync(req.query.path, 'utf8');
res.send({ name, content, path: req.query.path });
}
);
plugin.registerAPICall(
'post',
'/embark-api/folders',
(req, res) => {
try {
this.apiGuardBadFile(req.body.path);
} catch (error) {
return res.send({ error: error.message });
}
this.fs.mkdirpSync(req.body.path);
const name = path.basename(req.body.path);
res.send({ name, path: req.body.path });
}
);
plugin.registerAPICall(
'post',
'/embark-api/files',
(req, res) => {
try {
this.apiGuardBadFile(req.body.path);
} catch (error) {
return res.send({ error: error.message });
}
this.fs.writeFileSync(req.body.path, req.body.content, { encoding: 'utf8' });
const name = path.basename(req.body.path);
res.send({ name, path: req.body.path, content: req.body.content });
}
);
plugin.registerAPICall(
'delete',
'/embark-api/file',
(req, res) => {
try {
this.apiGuardBadFile(req.query.path, { ensureExists: true });
} catch (error) {
return res.send({ error: error.message });
}
this.fs.removeSync(req.query.path);
res.send();
}
);
plugin.registerAPICall(
'get',
'/embark-api/files',
(req, res) => {
const rootPath = dappPath();
const walk = (dir, filelist = []) => this.fs.readdirSync(dir).map(name => {
let isRoot = rootPath === dir;
if (this.fs.statSync(path.join(dir, name)).isDirectory()) {
return {
isRoot,
name,
dirname: dir,
path: path.join(dir, name),
isHidden: (name.indexOf('.') === 0 || name === "node_modules"),
children: fileTreeSort(walk(path.join(dir, name), filelist))
};
}
return {
name,
isRoot,
path: path.join(dir, name),
dirname: dir,
isHidden: (name.indexOf('.') === 0 || name === "node_modules")
};
});
const files = fileTreeSort(walk(dappPath()));
res.send(files);
}
);
}
apiGuardBadFile(pathToCheck, options = {ensureExists: false}) {
const dir = path.dirname(pathToCheck);
const error = new Error('Path is invalid');
if (options.ensureExists && !this.fs.existsSync(pathToCheck)) {
throw error;
}
if (!dir.startsWith(dappPath())) {
throw error;
}
}
}
module.exports = API;

View File

@ -1,402 +1,88 @@
const path = require('path');
const async = require('async');
import { __ } from 'embark-i18n';
import { dappPath, joinPath, LongRunningProcessTimer, fileTreeSort } from 'embark-utils';
import { ProcessLauncher } from 'embark-core';
const constants = require('embark-core/constants');
const WebpackConfigReader = require('./webpackConfigReader');
import { dappPath } from 'embark-utils';
const PipelineAPI = require('./api.js');
class Pipeline {
constructor(embark, options) {
this.embark = embark;
this.env = embark.config.env;
this.buildDir = embark.config.buildDir;
this.contractsFiles = embark.config.contractsFiles;
this.assetFiles = embark.config.assetFiles;
this.embarkConfig = embark.config.embarkConfig;
this.events = embark.events;
this.logger = embark.config.logger;
this.plugins = embark.config.plugins;
this.fs = embark.fs;
this.webpackConfigName = options.webpackConfigName;
this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
this.pipelineConfig = embark.config.pipelineConfig;
this.isFirstBuild = true;
this.useDashboard = options.useDashboard;
this.files = {}
// track changes to the pipeline config in the filesystem
this.events.on('config:load:pipeline', (pipelineConfig) => {
this.pipelineConfig = pipelineConfig;
this.api = new PipelineAPI(embark, options);
this.api.registerAPIs();
this.events.setCommandHandler('pipeline:generateAll', this.generateAll.bind(this));
this.events.setCommandHandler('pipeline:register', (params, cb) => {
this.files[dappPath(...params.path, params.file)] = params;
if (cb) { cb(); }
});
this.events.setCommandHandler('pipeline:build', (options, callback) => {
if(!this.pipelineConfig.enabled) {
return this.buildContracts([], callback);
}
this.build(options, callback);
});
this.events.setCommandHandler('pipeline:build:contracts', callback => this.buildContracts([], callback));
this.fs.removeSync(this.buildDir);
let plugin = this.plugins.createPlugin('deployment', {});
plugin.registerAPICall(
'get',
'/embark-api/file',
(req, res) => {
try {
this.apiGuardBadFile(req.query.path);
} catch (error) {
return res.send({error: error.message});
}
const name = path.basename(req.query.path);
const content = this.fs.readFileSync(req.query.path, 'utf8');
res.send({name, content, path: req.query.path});
}
);
plugin.registerAPICall(
'post',
'/embark-api/folders',
(req, res) => {
try {
this.apiGuardBadFile(req.body.path);
} catch (error) {
return res.send({error: error.message});
}
this.fs.mkdirpSync(req.body.path);
const name = path.basename(req.body.path);
res.send({name, path: req.body.path});
}
);
plugin.registerAPICall(
'post',
'/embark-api/files',
(req, res) => {
try {
this.apiGuardBadFile(req.body.path);
} catch (error) {
return res.send({error: error.message});
}
this.fs.writeFileSync(req.body.path, req.body.content, {encoding: 'utf8'});
const name = path.basename(req.body.path);
res.send({name, path: req.body.path, content: req.body.content});
}
);
plugin.registerAPICall(
'delete',
'/embark-api/file',
(req, res) => {
try {
this.apiGuardBadFile(req.query.path, {ensureExists: true});
} catch (error) {
return res.send({error: error.message});
}
this.fs.removeSync(req.query.path);
res.send();
}
);
plugin.registerAPICall(
'get',
'/embark-api/files',
(req, res) => {
const rootPath = dappPath();
const walk = (dir, filelist = []) => this.fs.readdirSync(dir).map(name => {
let isRoot = rootPath === dir;
if (this.fs.statSync(path.join(dir, name)).isDirectory()) {
return {
isRoot,
name,
dirname: dir,
path: path.join(dir, name),
isHidden: (name.indexOf('.') === 0 || name === "node_modules"),
children: fileTreeSort(walk(path.join(dir, name), filelist))
};
}
return {
name,
isRoot,
path: path.join(dir, name),
dirname: dir,
isHidden: (name.indexOf('.') === 0 || name === "node_modules")
};
});
const files = fileTreeSort(walk(dappPath()));
res.send(files);
}
);
}
apiGuardBadFile(pathToCheck, options = {ensureExists: false}) {
const dir = path.dirname(pathToCheck);
const error = new Error('Path is invalid');
if (options.ensureExists && !this.fs.existsSync(pathToCheck)) {
throw error;
}
if (!dir.startsWith(dappPath())) {
throw error;
}
}
build({modifiedAssets}, callback) {
let self = this;
const importsList = {};
let placeholderPage;
const contractsDir = dappPath(self.embarkConfig.generationDir, constants.dappArtifacts.contractsJs);
if (!self.assetFiles || !Object.keys(self.assetFiles).length) {
return self.buildContracts([], callback);
}
generateAll(cb) {
console.dir("generating all files");
async.waterfall([
function createPlaceholderPage(next) {
if (self.isFirstBuild) {
self.isFirstBuild = false;
return next();
}
self.events.request('placeholder:build', next);
},
(next) => self.buildContracts(importsList, next),
function createImportList(next) {
importsList["Embark/EmbarkJS"] = dappPath(self.embarkConfig.generationDir, constants.dappArtifacts.embarkjs);
importsList["Embark/contracts"] = contractsDir;
self.plugins.getPluginsProperty('imports', 'imports').forEach(importObject => {
let [importName, importLocation] = importObject;
importsList[importName] = importLocation;
(next) => {
this.plugins.runActionsForEvent("pipeline:generateAll:before", {}, (err) => {
next(err);
});
},
(next) => {
// TODO: make this async
for (let fileParams of Object.values(this.files)) {
if (fileParams.format === 'json') {
this.writeJSONFile(fileParams)
} else {
this.writeFile(fileParams)
}
}
next();
},
function shouldRunWebpack(next) {
// assuming we got here because an asset was changed, let's check our webpack config
// to see if the changed asset requires webpack to run
if (!(modifiedAssets && modifiedAssets.length)) return next(null, false);
const configReader = new WebpackConfigReader({webpackConfigName: self.webpackConfigName});
return configReader.readConfig((err, config) => {
if (err) return next(err);
if (typeof config !== 'object' || config === null) {
return next(__('Pipeline: bad webpack config, the resolved config was null or not an object'));
}
const shouldRun = modifiedAssets.some(modifiedAsset => config.module.rules.some(rule => rule.test.test(modifiedAsset)));
return next(null, !shouldRun);
});
},
function runWebpack(shouldNotRun, next) {
if (shouldNotRun) return next();
const assets = Object.keys(self.assetFiles).filter(key => key.match(/\.js$/));
if (!assets || !assets.length) {
return next();
}
let strAssets = '';
if (!self.useDashboard) {
assets.forEach(key => {
strAssets += ('\n ' + (joinPath(self.buildDir, key)).bold.dim);
});
}
const timer = new LongRunningProcessTimer(
self.logger,
'webpack',
'0',
`${'Pipeline:'.cyan} Bundling dapp using '${self.webpackConfigName}' config...${strAssets}`,
`${'Pipeline:'.cyan} Still bundling dapp using '${self.webpackConfigName}' config... ({{duration}})${strAssets}`,
`${'Pipeline:'.cyan} Finished bundling dapp in {{duration}}${strAssets}`,
{
showSpinner: !self.useDashboard,
interval: self.useDashboard ? 5000 : 1000,
longRunningThreshold: 15000
}
);
timer.start();
let built = false;
const webpackProcess = new ProcessLauncher({
embark: self.embark,
plugins: self.plugins,
modulePath: joinPath(__dirname, 'webpackProcess.js'),
logger: self.logger,
events: self.events,
exitCallback: code => {
if (!built) {
return next(`Webpack build exited with code ${code} before the process finished`);
}
if (code) {
self.logger.error(__('Webpack build process exited with code ', code));
}
}
});
webpackProcess.send({
action: constants.pipeline.init,
options: {
webpackConfigName: self.webpackConfigName,
pipelineConfig: self.pipelineConfig,
fs: self.embark.fs
}
});
webpackProcess.send({action: constants.pipeline.build, assets: self.assetFiles, importsList});
webpackProcess.once('result', constants.pipeline.built, (msg) => {
built = true;
webpackProcess.kill();
return next(msg.error);
});
webpackProcess.once('result', constants.pipeline.webpackDone, () => {
timer.end();
});
},
function assetFileWrite(next) {
async.eachOf(
// assetFileWrite should not process .js files
Object.keys(self.assetFiles)
.filter(key => !key.match(/\.js$/))
.reduce((obj, key) => {
obj[key] = self.assetFiles[key];
return obj;
}, {}),
function (files, targetFile, cb) {
const isDir = targetFile.slice(-1) === '/' || targetFile.slice(-1) === '\\' || targetFile.indexOf('.') === -1;
// if it's not a directory
if (!isDir) {
self.logger.info('Pipeline: '.cyan + __("writing file") + " " + (joinPath(self.buildDir, targetFile)).bold.dim);
}
async.map(
files,
function (file, fileCb) {
self.logger.trace("reading " + file.path);
file.content.then((fileContent) => {
self.runPlugins(file, fileContent, fileCb);
}).catch(fileCb);
},
function (err, contentFiles) {
if (err) {
self.logger.error('Pipeline: '.cyan + __('errors found while generating') + ' ' + targetFile);
}
let dir = targetFile.split('/').slice(0, -1).join('/');
self.logger.trace(`${'Pipeline:'.cyan} creating dir ` + joinPath(self.buildDir, dir));
self.fs.mkdirpSync(joinPath(self.buildDir, dir));
// if it's a directory
if (isDir) {
let targetDir = targetFile;
if (targetDir.slice(-1) !== '/') {
targetDir = targetDir + '/';
}
async.each(contentFiles, function (file, eachCb) {
let filename = file.path.replace(file.basedir + '/', '');
self.logger.info(`${'Pipeline:'.cyan} writing file ` + (joinPath(self.buildDir, targetDir, filename)).bold.dim);
self.fs.copy(file.path, joinPath(self.buildDir, targetDir, filename), {overwrite: true}, eachCb);
}, cb);
return;
}
let content = contentFiles.map(file => {
if (file === undefined) {
return "";
}
return file.content;
}).join("\n");
if (new RegExp(/^index.html?/i).test(targetFile)) {
targetFile = targetFile.replace('index', 'index-temp');
placeholderPage = targetFile;
}
self.fs.writeFile(joinPath(self.buildDir, targetFile), content, cb);
}
);
},
next
);
},
function removePlaceholderPage(next) {
let placeholderFile = joinPath(self.buildDir, placeholderPage);
self.fs.access(joinPath(self.buildDir, placeholderPage), (err) => {
if (err) return next(); // index-temp doesn't exist, do nothing
// rename index-temp.htm/l to index.htm/l, effectively replacing our placeholder page
// with the contents of the built index.html page
self.fs.move(placeholderFile, placeholderFile.replace('index-temp', 'index'), {overwrite: true}, next);
(next) => {
this.plugins.runActionsForEvent("pipeline:generateAll:after", {}, (err) => {
next(err);
});
}
], callback);
], () => {
cb();
});
}
buildContracts(importsList, cb) {
writeJSONFile(params) {
const self = this;
const dir = dappPath(...params.path);
const filename = dappPath(...params.path, params.file);
const content = params.content;
async.waterfall([
function makeDirectory(next) {
self.fs.mkdirp(dappPath(self.buildDir, 'contracts'), err => next(err));
self.fs.mkdirp(dir, err => next(err));
},
function getContracts(next) {
self.events.request('contracts:list', next);
},
function writeContractsJSON(contracts, next) {
async.each(contracts, (contract, eachCb) => {
self.fs.writeJson(dappPath(
self.buildDir,
'contracts', contract.className + '.json'
), contract, {spaces: 2}, eachCb);
}, () => next(null, contracts));
},
function writeContractJS(contracts, next) {
const contractsDir = dappPath(self.embarkConfig.generationDir, constants.dappArtifacts.contractsJs);
self.fs.mkdirp(contractsDir, err => {
if (err) return next(err);
// Create a file index.js that requires all contract files
// Used to enable alternate import syntax:
// e.g. import {Token} from 'Embark/contracts'
// e.g. import * as Contracts from 'Embark/contracts'
let importsHelperFile = self.fs.createWriteStream(joinPath(contractsDir, 'index.js'));
importsHelperFile.write('module.exports = {\n');
async.eachOf(contracts, (contract, idx, eachCb) => {
self.events.request('code-generator:contract', contract.className, (err, contractPath) => {
if (err) {
return eachCb(err);
}
importsList["Embark/contracts/" + contract.className] = dappPath(contractPath);
// add the contract to the exports list to support alternate import syntax
importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default,\n`);
eachCb();
});
}, () => {
importsHelperFile.write('\n};'); // close the module.exports = {}
importsHelperFile.close(next); // close the write stream
});
});
function writeContractsJSON(next) {
self.fs.writeJson(filename, content, { spaces: 2 }, () => { next() });
}
], cb);
], () => {
});
}
runPlugins(file, fileContent, fileCb) {
// TODO: can be refactored by joining with method above
writeFile(params) {
const self = this;
if (self.pipelinePlugins.length <= 0) {
return fileCb(null, {content: fileContent, path: file.path, basedir: file.basedir, modified: true});
}
async.eachSeries(self.pipelinePlugins, (plugin, pluginCB) => {
if (file.options && file.options.skipPipeline) {
return pluginCB();
}
const dir = dappPath(...params.path);
const filename = dappPath(...params.path, params.file);
const content = params.content;
fileContent = plugin.runPipeline({targetFile: file.path, source: fileContent});
file.modified = true;
pluginCB();
}, err => {
if (err) {
self.logger.error(err.message);
async.waterfall([
function makeDirectory(next) {
self.fs.mkdirp(dir, err => next(err));
},
function writeFile(next) {
self.fs.writeFile(filename, content, (err) => { next(err, true) });
}
return fileCb(null, {content: fileContent, path: file.path, basedir: file.basedir, modified: true});
], () => {
});
}

View File

@ -1,5 +1,7 @@
const {escapeHtml, LogHandler} = require('embark-utils');
// TODO: looks unnecessary, should be moved to a common module together with embark-listener
class ProcessLogsApi {
constructor({embark, processName, silent}) {
this.embark = embark;

View File

@ -14,11 +14,11 @@ class SpecialConfigs {
this.embark = embark;
this.config = embark.config;
this.registerBeforeAllDeployAction();
this.registerAfterDeployAction();
this.registerBeforeDeployAction();
this.registerOnDeployAction();
this.registerDeployIfAction();
// this.registerBeforeAllDeployAction();
// this.registerAfterDeployAction();
// this.registerBeforeDeployAction();
// this.registerOnDeployAction();
// this.registerDeployIfAction();
}
replaceWithENSAddress(cmd, callback) {

View File

@ -69,9 +69,10 @@ class SolcTest extends Test {
async.series(fns, next);
},
function resetEmbarkJs(file, next) {
self.events.request("runcode:embarkjs:reset", (err) => {
next(err, file);
});
// self.events.request("runcode:embarkjs:reset", (err) => {
// next(err, file);
// });
next(err, file);
}
], cb);
}

View File

@ -169,9 +169,8 @@ class Test {
}
this.firstRunConfig = false;
this.events.request("blockchain:ready", () => {
this.events.request("code-generator:embarkjs:build", () => {
this.events.request("runcode:embarkjs:reset", callback);
});
// this.events.request("runcode:embarkjs:reset", callback);
callback();
});
});
}

View File

@ -21,6 +21,7 @@ class Watcher {
this.events.setCommandHandler('watcher:restart', () => this.restart());
}
// TODO: it needs to be more agnostic, the files to watch should be registered through the plugin api
start() {
let self = this;
// TODO: should come from the config object instead of reading the file

View File

@ -20,21 +20,28 @@ class EmbarkWeb3 {
async addWeb3ToEmbarkJS() {
let blockchainConnectorReady = false;
const web3LocationPromise = this.getWeb3Location();
code += "\nEmbarkJS.Blockchain.registerProvider('web3', embarkJSConnectorWeb3);";
// code += "\nEmbarkJS.Blockchain.setProvider('web3', {});";
this.events.setCommandHandler('blockchain:connector:ready', (cb) => {
if (blockchainConnectorReady) {
return cb();
code += "\nEmbarkJS.Blockchain.setProvider('web3', {web3});";
this.events.request('runcode:eval', code, (err) => {
if (err) {
return cb(err);
}
this.events.once("blockchain:connector:ready", () => {
cb();
});
});
}
web3LocationPromise.then((_web3Location) => {
blockchainConnectorReady = true;
this.events.emit('blockchain:connector:ready');
});
registerWeb3Help() {
this.events.request('console:register:helpCmd', {
cmdName: "web3",
cmdHelp: __("instantiated web3.js object configured to the current environment")
}, () => { })
}
// ===============
// ===============
// ===============
let web3Location = await web3LocationPromise;
web3Location = normalizePath(web3Location, true);

View File

@ -0,0 +1,88 @@
const {parallel} = require('async');
const {fromEvent} = require('rxjs');
const {map, takeUntil} = require('rxjs/operators');
import whisper from 'embarkjs-whisper';
const sendMessage = whisper.real_sendMessage;
const listenTo = whisper.real_listenTo;
class API {
constructor(embark, web3) {
this.embark = embark;
this.logger = embark.logger;
this.web3 = web3;
this.registerAPICalls();
}
registerAPICalls() {
const self = this;
if (self.apiCallsRegistered) {
return;
}
self.apiCallsRegistered = true;
let symKeyID, sig;
parallel([
function(paraCb) {
self.web3.shh.newSymKey((err, id) => {
symKeyID = id;
paraCb(err);
});
},
function(paraCb) {
self.web3.shh.newKeyPair((err, id) => {
sig = id;
paraCb(err);
});
}
], (err) => {
if (err) {
self.logger.error('Error getting Whisper keys:', err.message || err);
return;
}
self.embark.registerAPICall(
'post',
'/embark-api/communication/sendMessage',
(req, res) => {
sendMessage({
topic: req.body.topic,
data: req.body.message,
sig,
symKeyID,
fromAscii: self.web3.utils.asciiToHex,
toHex: self.web3.utils.toHex,
post: self.web3.shh.post
}, (err, result) => {
if (err) {
return res.status(500).send({error: err});
}
res.send(result);
});
});
self.embark.registerAPICall(
'ws',
'/embark-api/communication/listenTo/:topic',
(ws, req) => {
const obs = listenTo({
toAscii: self.web3.utils.hexToAscii,
toHex: self.web3.utils.toHex,
topic: req.params.topic,
sig,
subscribe: self.web3.shh.subscribe,
symKeyID
}).pipe(takeUntil(fromEvent(ws, 'close').pipe(map(() => (
delete self.webSocketsChannels[req.params.topic]
)))));
self.webSocketsChannels[req.params.topic] = obs;
obs.subscribe(data => {
ws.send(JSON.stringify(data));
});
});
});
}
}
module.exports = API;

View File

@ -1,21 +1,12 @@
/* global __dirname module require setTimeout */
import { __ } from 'embark-i18n';
import {dappPath, canonicalHost, defaultHost} from 'embark-utils';
let Web3 = require('web3');
const {parallel} = require('async');
const {fromEvent} = require('rxjs');
const {map, takeUntil} = require('rxjs/operators');
const constants = require('embark-core/constants');
import * as path from 'path';
const API = require('./api.js');
const EMBARK_RESOURCE_ORIGIN = "http://embark";
import whisper from 'embarkjs-whisper';
const sendMessage = whisper.real_sendMessage;
const listenTo = whisper.real_listenTo;
class Whisper {
constructor(embark, options) {
this.logger = embark.logger;
@ -37,31 +28,49 @@ class Whisper {
return;
}
this.connectToProvider();
this.api = new API(embark, this.web3);
this.api.registerAPICalls();
this.events.request('processes:register', 'whisper', (cb) => {
this.waitForWeb3Ready(() => {
this.web3.shh.getInfo((err) => {
if (err) {
const message = err.message || err;
if (message.indexOf('not supported') > -1) {
this.logger.error('Whisper is not supported on your node. Are you using the simulator?');
return this.logger.trace(message);
}
}
this.setServiceCheck();
this.addSetProvider();
this.registerAPICalls();
cb();
});
});
// ================
// TODO:
// figure out best way to detect is a node exists or launch a whisper process or wait for the blockchain process
// ================
// this.events.on("blockchain:ready", this.executeEmbarkJSBlockchain.bind(this));
this.setServiceCheck();
// TODO: see above, not ideal to do this, need engine.start process
// can also register service and instead react to it and connect
// this.waitForWeb3Ready(() => {
// this.registerAndSetWhisper();
// });
this.events.on("blockchain:ready", () => {
this.registerAndSetWhisper();
});
// TODO: it will have the issue of waiting for the ipfs to start when the code is generator
// TODO: could be solved by having a list of services to wait on before attempting to execute code in the console
this.addWhisperToEmbarkJS();
// ===============================
// this.connectToProvider();
this.events.request('processes:launch', 'whisper');
// this.events.request('processes:register', 'whisper', (cb) => {
// this.waitForWeb3Ready(() => {
// this.web3.shh.getInfo((err) => {
// if (err) {
// const message = err.message || err;
// if (message.indexOf('not supported') > -1) {
// this.logger.error('Whisper is not supported on your node. Are you using the simulator?');
// return this.logger.trace(message);
// }
// }
// this.setServiceCheck();
// this.addWhisperToEmbarkJS();
// this.addSetProvider();
// this.registerAPICalls();
// cb();
// });
// });
// });
// this.events.request('processes:launch', 'whisper');
}
connectToProvider() {
@ -75,6 +84,57 @@ class Whisper {
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: EMBARK_RESOURCE_ORIGIN}}));
}
registerAndSetWhisper() {
if (this.communicationConfig === {}) {
return;
}
if ((this.communicationConfig.available_providers.indexOf('whisper') < 0) && (this.communicationConfig.provider !== 'whisper' || this.communicationConfig.enabled !== true)) {
return;
}
// let linkedModulePath = path.join(this.modulesPath, 'embarkjs-whisper');
// if (process.platform === 'win32') linkedModulePath = linkedModulePath.replace(/\\/g, '\\\\');
// const code = `
// const __embarkWhisperNewWeb3 = EmbarkJS.isNode ? require('${linkedModulePath}') : require('embarkjs-whisper');
// EmbarkJS.Messages.registerProvider('whisper', __embarkWhisperNewWeb3.default || __embarkWhisperNewWeb3);
// `;
let code = `
const __embarkWhisperNewWeb3 = require('embarkjs-whisper');
EmbarkJS.Messages.registerProvider('whisper', __embarkWhisperNewWeb3.default || __embarkWhisperNewWeb3);
`;
let connection = this.communicationConfig.connection || {};
if (!(this.communicationConfig.provider === 'whisper' && this.communicationConfig.enabled === true)) {
return this.events.request('runcode:eval', code, () => {
});
}
// todo: make the add code a function as well
const config = {
server: canonicalHost(connection.host || defaultHost),
port: connection.port || '8546',
type: connection.type || 'ws'
};
code += `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(config)});`;
// this.embark.addCodeToEmbarkJS(code);
this.events.request('runcode:eval', code, (err) => {
// if (err) {
// return cb(err);
// }
});
}
// ===============================
// ===============================
// ===============================
// ===============================
// ===============================
// ===============================
waitForWeb3Ready(cb) {
if (this.web3Ready) {
return cb();
@ -119,37 +179,6 @@ class Whisper {
});
}
addWhisperToEmbarkJS() {
// TODO: make this a shouldAdd condition
if (this.communicationConfig === {}) {
return;
}
if ((this.communicationConfig.available_providers.indexOf('whisper') < 0) && (this.communicationConfig.provider !== 'whisper' || this.communicationConfig.enabled !== true)) {
return;
}
let linkedModulePath = path.join(this.modulesPath, 'embarkjs-whisper');
if (process.platform === 'win32') linkedModulePath = linkedModulePath.replace(/\\/g, '\\\\');
this.events.request('version:downloadIfNeeded', 'embarkjs-whisper', (err, location) => {
if (err) {
this.logger.error(__('Error downloading embarkjs-whisper'));
throw err;
}
const code = `
const __embarkWhisperNewWeb3 = require('${linkedModulePath}');
EmbarkJS.Messages.registerProvider('whisper', __embarkWhisperNewWeb3.default || __embarkWhisperNewWeb3);
`;
this.embark.addProviderInit("communication", code, () => { return true; });
this.embark.addConsoleProviderInit("communication", code, () => { return true; });
this.embark.addGeneratedCode((cb) => {
return cb(null, code, `embarkjs-whisper`, location);
});
});
}
addSetProvider() {
let connection = this.communicationConfig.connection || {};
const shouldInit = (communicationConfig) => {
@ -162,13 +191,8 @@ class Whisper {
port: connection.port || '8546',
type: connection.type || 'ws'
};
// TODO: fix storage to also use addProviderInit
// execute code called addProviderInit WHEN contracts have been deployed etc..
// TODO: diff between addConsoleProviderInit and addProviderInit
// or maybe best way is todo everything in the module
const code = `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(config)});`;
this.embark.addProviderInit('communication', code, shouldInit);
const consoleConfig = Object.assign({}, config, {providerOptions: {headers: {Origin: EMBARK_RESOURCE_ORIGIN}}});
@ -176,72 +200,6 @@ class Whisper {
this.embark.addConsoleProviderInit('communication', consoleCode, shouldInit);
}
registerAPICalls() {
const self = this;
if (self.apiCallsRegistered) {
return;
}
self.apiCallsRegistered = true;
let symKeyID, sig;
parallel([
function(paraCb) {
self.web3.shh.newSymKey((err, id) => {
symKeyID = id;
paraCb(err);
});
},
function(paraCb) {
self.web3.shh.newKeyPair((err, id) => {
sig = id;
paraCb(err);
});
}
], (err) => {
if (err) {
self.logger.error('Error getting Whisper keys:', err.message || err);
return;
}
self.embark.registerAPICall(
'post',
'/embark-api/communication/sendMessage',
(req, res) => {
sendMessage({
topic: req.body.topic,
data: req.body.message,
sig,
symKeyID,
fromAscii: self.web3.utils.asciiToHex,
toHex: self.web3.utils.toHex,
post: self.web3.shh.post
}, (err, result) => {
if (err) {
return res.status(500).send({error: err});
}
res.send(result);
});
});
self.embark.registerAPICall(
'ws',
'/embark-api/communication/listenTo/:topic',
(ws, req) => {
const obs = listenTo({
toAscii: self.web3.utils.hexToAscii,
toHex: self.web3.utils.toHex,
topic: req.params.topic,
sig,
subscribe: self.web3.shh.subscribe,
symKeyID
}).pipe(takeUntil(fromEvent(ws, 'close').pipe(map(() => (
delete self.webSocketsChannels[req.params.topic]
)))));
self.webSocketsChannels[req.params.topic] = obs;
obs.subscribe(data => {
ws.send(JSON.stringify(data));
});
});
});
}
}
module.exports = Whisper;

View File

@ -10,8 +10,8 @@ module.exports = (api) => {
const node = cloneDeep(base);
Object.assign(node, {
ignore: [
'src/lib/modules/pipeline/babel-loader-overrides.js',
'src/lib/modules/pipeline/webpack.config.js'
'src/lib/modules/basic-pipeline/babel-loader-overrides.js',
'src/lib/modules/basic-pipeline/webpack.config.js'
]
});

View File

@ -135,17 +135,27 @@ class Engine {
pipelineService(_options) {
const self = this;
this.registerModulePackage('embark-pipeline', {
this.registerModulePackage('embark-pipeline', { plugins: this.plugins });
this.registerModule('basic-pipeline', {
plugins: this.plugins,
webpackConfigName: this.webpackConfigName,
useDashboard: this.useDashboard
});
this.events.on('code-generator-ready', function (modifiedAssets) {
self.events.request('code', function (abi, contractsJSON) {
self.events.request('pipeline:build', {abi, contractsJSON, modifiedAssets}, () => {
self.events.emit('outputDone');
});
// this.events.on('code-generator-ready', function (modifiedAssets) {
// self.events.request('code', function (abi, contractsJSON) {
// self.events.request('pipeline:build', {abi, contractsJSON, modifiedAssets}, () => {
// self.events.emit('outputDone');
// });
// });
// });
// TODO: move this to cmd_controller and define all such behaviour there
this.events.on('contracts:deploy:afterAll', () => {
self.events.request('pipeline:generateAll', () => {
console.dir("outputDone")
self.events.emit('outputDone');
});
});
})
}
serviceMonitor() {
@ -180,32 +190,30 @@ class Engine {
}
codeGeneratorService(_options) {
let self = this;
this.registerModulePackage('embark-code-generator', {plugins: self.plugins, env: self.env});
const generateCode = function (modifiedAssets, cb) {
self.events.request("code-generator:embarkjs:build", () => {
self.events.emit('code-generator-ready', modifiedAssets);
cb();
});
};
const cargo = async.cargo((tasks, callback) => {
const modifiedAssets = tasks.map(task => task.modifiedAsset).filter(asset => asset); // filter null elements
generateCode(modifiedAssets, () => {
if (this.context.includes('test')) {
return callback();
}
self.events.once('outputDone', callback);
});
});
const addToCargo = function (modifiedAsset) {
cargo.push({modifiedAsset});
};
this.events.on('contractsDeployed', addToCargo);
this.events.on('blockchainDisabled', addToCargo);
this.events.on('asset-changed', addToCargo);
return;
// let self = this;
//
// this.registerModulePackage('embark-code-generator', {plugins: self.plugins, env: self.env});
//
// const generateCode = function (modifiedAssets) {
// // self.events.request("module:storage:onReady", () => {
// self.events.request("code-generator:embarkjs:build", () => {
// self.events.emit('code-generator-ready', modifiedAssets);
// });
// // });
// };
// const cargo = async.cargo((tasks, callback) => {
// const modifiedAssets = tasks.map(task => task.modifiedAsset).filter(asset => asset); // filter null elements
// generateCode(modifiedAssets);
// self.events.once('outputDone', callback);
// });
// const addToCargo = function (modifiedAsset) {
// cargo.push({modifiedAsset});
// };
//
// this.events.on('contractsDeployed', addToCargo);
// this.events.on('blockchainDisabled', addToCargo);
// this.events.on('asset-changed', addToCargo);
}
setupCompilerAndContractsManagerService(options) {
@ -269,23 +277,23 @@ class Engine {
}
storageService(_options) {
this.registerModulePackage('embark-ipfs');
this.registerModulePackage('embark-swarm');
this.registerModulePackage('embark-storage', {plugins: this.plugins});
// this.registerModulePackage('embark-ipfs');
// this.registerModulePackage('embark-swarm');
// this.registerModulePackage('embark-storage', {plugins: this.plugins});
this.events.setCommandHandler("module:storage:reset", (cb) => {
async.parallel([
(paraCb) => {
this.events.request("module:ipfs:reset", paraCb);
},
(paraCb) => {
this.events.request("module:swarm:reset", paraCb);
},
(paraCb) => {
this.events.request("module:storageJS:reset", paraCb);
}
], cb);
});
// this.events.setCommandHandler("module:storage:reset", (cb) => {
// async.parallel([
// (paraCb) => {
// this.events.request("module:ipfs:reset", paraCb);
// },
// (paraCb) => {
// this.events.request("module:swarm:reset", paraCb);
// },
// (paraCb) => {
// this.events.request("module:storageJS:reset", paraCb);
// }
// ], cb);
// });
}
web3Service(options) {
@ -307,6 +315,7 @@ class Engine {
});
this.registerModulePackage('embark-whisper');
this.registerModule('web3', { plugins: this.plugins });
}
libraryManagerService(_options) {

View File

@ -55,8 +55,13 @@ EventEmitter.prototype.request = function() {
log("requesting: ", requestName);
warnIfLegacy(requestName);
if (this._events && !this._events['request:' + requestName]) {
console.log("made request without listener: " + requestName)
}
const listenerName = 'request:' + requestName;
// TODO: remove this, it will lead to illusion of things working when this situatio shouldnt' hapepn in the first place
// if we don't have a command handler set for this event yet,
// store it and fire it once a command handler is set
if (!this.listeners(listenerName).length) {

View File

@ -161,6 +161,7 @@ Plugins.prototype.getPluginsProperty = function(pluginType, property, sub_proper
return matchingProperties.reduce((a,b) => { return a.concat(b); }) || [];
};
// TODO: because this is potentially hanging, we should issue a trace warning if the event does not exists
Plugins.prototype.runActionsForEvent = function(eventName, args, cb) {
if (typeof (args) === 'function') {
cb = args;

View File

@ -0,0 +1,257 @@
const async = require('async');
import { __ } from 'embark-i18n';
import { ProcessLauncher } from 'embark-core';
import { dappPath, joinPath, LongRunningProcessTimer } from 'embark-utils';
const constants = require('embark-core/constants');
const WebpackConfigReader = require('./webpackConfigReader');
class BasicPipeline {
constructor(embark, options) {
this.embark = embark;
this.assetFiles = embark.config.assetFiles;
this.isFirstBuild = true;
this.embarkConfig = embark.config.embarkConfig;
// TODO: why god why
this.useDashboard = options.useDashboard;
this.fs = embark.fs;
this.webpackConfigName = options.webpackConfigName;
this.env = embark.config.env;
this.buildDir = embark.config.buildDir;
this.contractsFiles = embark.config.contractsFiles;
this.embarkConfig = embark.config.embarkConfig;
this.logger = embark.logger;
this.events = embark.events;
this.plugins = options.plugins;
this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
this.pipelineConfig = embark.config.pipelineConfig;
let plugin = this.plugins.createPlugin('basic-pipeline', {});
plugin.registerActionForEvent("pipeline:generateAll:after", this.webpackAssets.bind(this));
// track changes to the pipeline config in the filesystem
this.events.on('config:load:pipeline', (pipelineConfig) => {
this.pipelineConfig = pipelineConfig;
});
}
webpackAssets(params, done) {
console.dir("=======================> webpackAssets");
let self = this;
let placeholderPage;
const importsList = {};
if (!self.assetFiles || !Object.keys(self.assetFiles).length) {
return done(); // no assetFiles so nothing to do
}
let modifiedAssets = self.assetFiles;
async.waterfall([
(next) => {
console.dir("=======================> importsList");
importsList["Embark/EmbarkJS"] = dappPath(self.embarkConfig.generationDir, 'embarkjs.js');
importsList["Embark/contracts"] = this.embarkConfig.generationDir;
next();
},
function shouldRunWebpack(next) {
console.dir("=======================> shouldRunWebpack");
// assuming we got here because an asset was changed, let's check our webpack config
// to see if the changed asset requires webpack to run
if (!(modifiedAssets && modifiedAssets.length)) return next(null, false);
const configReader = new WebpackConfigReader({webpackConfigName: self.webpackConfigName});
return configReader.readConfig((err, config) => {
if (err) return next(err);
if (typeof config !== 'object' || config === null) {
return next(__('Pipeline: bad webpack config, the resolved config was null or not an object'));
}
const shouldRun = modifiedAssets.some(modifiedAsset => config.module.rules.some(rule => rule.test.test(modifiedAsset)));
return next(null, !shouldRun);
});
},
function runWebpack(shouldNotRun, next) {
console.dir("=======================> runWebpack");
if (shouldNotRun) return next();
const assets = Object.keys(self.assetFiles).filter(key => key.match(/\.js$/));
if (!assets || !assets.length) {
return next();
}
let strAssets = '';
if (!self.useDashboard) {
assets.forEach(key => {
strAssets += ('\n ' + (joinPath(self.buildDir, key)).bold.dim);
});
}
const timer = new LongRunningProcessTimer(
self.logger,
'webpack',
'0',
`${'Pipeline:'.cyan} Bundling dapp using '${self.webpackConfigName}' config...${strAssets}`,
`${'Pipeline:'.cyan} Still bundling dapp using '${self.webpackConfigName}' config... ({{duration}})${strAssets}`,
`${'Pipeline:'.cyan} Finished bundling dapp in {{duration}}${strAssets}`,
{
showSpinner: !self.useDashboard,
interval: self.useDashboard ? 5000 : 1000,
longRunningThreshold: 15000
}
);
timer.start();
let built = false;
const webpackProcess = new ProcessLauncher({
embark: self.embark,
plugins: self.plugins,
modulePath: joinPath(__dirname, 'webpackProcess.js'),
logger: self.logger,
events: self.events,
exitCallback: code => {
if (!built) {
return next(`Webpack build exited with code ${code} before the process finished`);
}
if (code) {
self.logger.error(__('Webpack build process exited with code ', code));
}
}
});
webpackProcess.send({
action: constants.pipeline.init,
options: {
webpackConfigName: self.webpackConfigName,
pipelineConfig: self.pipelineConfig,
fs: self.embark.fs
}
});
webpackProcess.send({action: constants.pipeline.build, assets: self.assetFiles, importsList});
webpackProcess.once('result', constants.pipeline.built, (msg) => {
built = true;
webpackProcess.kill();
return next(msg.error);
});
webpackProcess.once('result', constants.pipeline.webpackDone, () => {
timer.end();
});
},
function assetFileWrite(next) {
console.dir("=======================> assetFileWrite 136");
async.eachOf(
// assetFileWrite should not process .js files
Object.keys(self.assetFiles)
.filter(key => !key.match(/\.js$/))
.reduce((obj, key) => {
obj[key] = self.assetFiles[key];
return obj;
}, {}),
function (files, targetFile, cb) {
console.dir("== eachOf: " + targetFile);
const isDir = targetFile.slice(-1) === '/' || targetFile.slice(-1) === '\\' || targetFile.indexOf('.') === -1;
// if it's not a directory
if (!isDir) {
self.logger.info('Pipeline: '.cyan + __("_1_ writing file") + " " + (joinPath(self.buildDir, targetFile)).bold.dim);
}
console.dir("async.map")
// async.map(
async.mapLimit(
files,
1,
function (file, fileCb) {
self.logger.trace("reading " + file.path);
console.dir(":::::::: reading " + file.path);
file.content.then((fileContent) => {
self.runPlugins(file, fileContent, fileCb);
}).catch(fileCb);
},
function (err, contentFiles) {
try {
if (err) {
self.logger.error('Pipeline: '.cyan + __('errors found while generating') + ' ' + targetFile);
console.dir(err);
}
let dir = targetFile.split('/').slice(0, -1).join('/');
self.logger.trace(`${'Pipeline:'.cyan} creating dir ` + joinPath(self.buildDir, dir));
self.fs.mkdirpSync(joinPath(self.buildDir, dir));
// if it's a directory
if (isDir) {
console.dir("---> isDir")
let targetDir = targetFile;
if (targetDir.slice(-1) !== '/') {
targetDir = targetDir + '/';
}
console.dir("===> contentFiles")
console.dir(contentFiles)
console.dir("----------")
async.each(contentFiles, function (file, eachCb) {
let filename = file.path.replace(file.basedir + '/', '');
self.logger.info(`${'Pipeline:'.cyan} __ writing file ` + (joinPath(self.buildDir, targetDir, filename)).bold.dim);
self.fs.copy(file.path, joinPath(self.buildDir, targetDir, filename), {overwrite: true}, eachCb);
}, cb);
return;
}
console.dir("---> not a dir")
let content = contentFiles.map(file => {
if (file === undefined) {
return "";
}
return file.content;
}).join("\n");
if (new RegExp(/^index.html?/i).test(targetFile)) {
targetFile = targetFile.replace('index', 'index-temp');
placeholderPage = targetFile;
}
console.dir("--> writing file")
self.fs.writeFile(joinPath(self.buildDir, targetFile), content, cb);
} catch(err) {
console.dir(err)
}
}
);
},
next
);
},
function removePlaceholderPage(next) {
console.dir("=======================> removePlaceholderPage");
let placeholderFile = joinPath(self.buildDir, placeholderPage);
self.fs.access(joinPath(self.buildDir, placeholderPage), (err) => {
if (err) return next(); // index-temp doesn't exist, do nothing
// rename index-temp.htm/l to index.htm/l, effectively replacing our placeholder page
// with the contents of the built index.html page
self.fs.move(placeholderFile, placeholderFile.replace('index-temp', 'index'), {overwrite: true}, next);
});
}
], done);
}
runPlugins(file, fileContent, fileCb) {
const self = this;
if (self.pipelinePlugins.length <= 0) {
return fileCb(null, {content: fileContent, path: file.path, basedir: file.basedir, modified: true});
}
async.eachSeries(self.pipelinePlugins, (plugin, pluginCB) => {
if (file.options && file.options.skipPipeline) {
return pluginCB();
}
fileContent = plugin.runPipeline({targetFile: file.path, source: fileContent});
file.modified = true;
pluginCB();
}, err => {
if (err) {
self.logger.error(err.message);
}
return fileCb(null, {content: fileContent, path: file.path, basedir: file.basedir, modified: true});
});
}
}
module.exports = BasicPipeline;

View File

@ -0,0 +1,181 @@
require('ejs');
import { embarkPath } from "embark-utils";
import { transform } from "@babel/core";
const Templates = {
vanilla_contract: require('./vanilla-contract.js.ejs')
};
// TODO: this should be moved to the embark-web3 module from master
class Web3Plugin {
constructor(embark, options) {
this.embarkConfig = embark.config.embarkConfig;
this.logger = embark.logger;
this.events = embark.events;
this.plugins = options.plugins;
let plugin = this.plugins.createPlugin('web3plugin', {});
plugin.registerActionForEvent("deploy:contract:deployed", this.registerInVm.bind(this));
plugin.registerActionForEvent("deploy:contract:deployed", this.addContractJSONToPipeline.bind(this));
plugin.registerActionForEvent("deploy:contract:deployed", this.addContractFileToPipeline.bind(this));
plugin.registerActionForEvent("pipeline:generateAll:before", this.addEmbarkJSNode.bind(this));
plugin.registerActionForEvent("pipeline:generateAll:before", this.addContractIndexToPipeline.bind(this));
}
registerInVm(params, cb) {
let contract = params.contract;
let abi = JSON.stringify(contract.abiDefinition);
let gasLimit = 6000000;
let contractCode = Templates.vanilla_contract({ className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit });
this.events.request('runcode:eval', contractCode, (err) => {
if (err) {
return cb(err);
}
this.events.request('runcode:eval', contract.className, (err, result) => {
if (err) {
return cb(err);
}
this.events.emit("runcode:register", contract.className, result, () => { cb() });
});
});
}
addContractJSONToPipeline(params, cb) {
// TODO: check if this is correct json object to generate
const contract = params.contract;
this.events.request("pipeline:register", {
path: [this.embarkConfig.buildDir, 'contracts'],
file: contract.className + '.json',
format: 'json',
content: contract
}, cb);
}
addContractFileToPipeline(params, cb) {
const contract = params.contract;
const contractName = contract.className;
console.dir("--------------");
console.dir("--------------");
console.dir(contract.className);
const contractJSON = contract.abiDefinition;
const contractCode = `
"use strict";
const isNode = (typeof process !== 'undefined' && process.versions && process.versions.node);
const lib = isNode ? '../embarkjs.node' : '../embarkjs';
const EmbarkJSNode = isNode && require('../embarkjs.node');
let EmbarkJSBrowser;
try {
EmbarkJSBrowser = require('../embarkjs').default;
} catch(e) {};
const EmbarkJS = isNode ? EmbarkJSNode : EmbarkJSBrowser;
let ${contractName}JSONConfig = ${JSON.stringify(contractJSON)};
let ${contractName} = new EmbarkJS.Blockchain.Contract(${contractName}JSONConfig);
module.exports = ${contractName};
`.trim().replace(/^[\t\s]+/gm, '');
this.events.request("pipeline:register", {
path: [this.embarkConfig.generationDir, 'contracts'],
file: contract.className + '.js',
format: 'js',
content: contractCode
}, cb);
}
addContractIndexToPipeline(_params, cb) {
this.events.request("contracts:list", (err, contracts) => {
contracts.forEach(console.dir)
let imports = contracts.filter(c => c.deployedAddress || c.deploy).map((c) => {
return `"${c.className}": require('./${c.className}').default`;
}).join(",\n");
let code = 'module.exports = {\n';
code += imports;
code += '\n};';
this.events.request("pipeline:register", {
path: [this.embarkConfig.generationDir, 'contracts'],
file: 'index.js',
format: 'js',
content: code
}, cb);
});
}
// TODO: ideally shouldn't be done here
addEmbarkJSNode(_params, cb) {
let embarkjsCode = '';
// TODO: the symblink is unclear at this point, but if needed, it should be done at the pipeline through a request
// TODO: embarkjs stuff should also be in a embark-embarkjs module
// self.generateSymlink(location, 'embarkjs', (err, symlinkDest) => {
// if (err) {
// self.logger.error(__('Error creating a symlink to EmbarkJS'));
// return next(err);
// }
// embarkjsCode += `\nconst EmbarkJS = require("${symlinkDest}").default || require("${symlinkDest}");`;
embarkjsCode += `\nconst EmbarkJS = require("embarkjs").default;`;
// embarkjsCode += `\nEmbarkJS.environment = '${self.env}';`;
embarkjsCode += "\nglobal.EmbarkJS = EmbarkJS;";
// next();
// });
let code = "";
code += "\n" + embarkjsCode + "\n";
code += "\nexport default EmbarkJS;";
code += "\nif (typeof module !== 'undefined' && module.exports) {" +
"\n\tmodule.exports = EmbarkJS;" +
"\n}";
code += '\n/* eslint-enable */\n';
// TODO: should be done in async.waterfall
this.events.request("pipeline:register", {
path: [this.embarkConfig.generationDir],
file: 'embarkjs.js',
format: 'js',
content: code
});
// embark.js
// self.generateArtifact(code, constants.dappArtifacts.embarkjs, '', next);
transform(code, {
cwd: embarkPath(),
"presets": [
[
"@babel/preset-env", {
"targets": {
"node": "8.11.3"
}
}
]
]
}, (err, result) => {
if (err) {
return cb(err);
}
this.events.request("pipeline:register", {
path: [this.embarkConfig.generationDir],
file: 'embarkjs.node.js',
format: 'js',
content: code
}, cb);
// self.generateArtifact(result.code, constants.dappArtifacts.embarkjsnode, '', next);
});
// embark.node.js
}
}
module.exports = Web3Plugin;

View File

@ -0,0 +1,9 @@
<%- className %>Abi = <%- abi %>;
<%- className %> = new web3.eth.Contract(<%- className %>Abi);
<%- className %>.options.address = '<%- contract.deployedAddress %>';
<%- className %>.address = '<%- contract.deployedAddress %>';
<%- className %>.options.from = web3.eth.defaultAccount;
<% if (gasLimit != false) { %>
<%- className %>.options.gas = <%- gasLimit %>;
<%- className %>.options.gasLimit = <%- gasLimit %>;
<% } %>

View File

@ -0,0 +1,40 @@
{
"name": "embarkjs-connector-web3",
"version": "4.1.0-beta.2",
"author": "Iuri Matias <iuri.matias@gmail.com>",
"contributors": [],
"description": "Web3.js Connector for EmbarkJS",
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/embarkjs-connector-web3#readme",
"bugs": "https://github.com/embark-framework/embark/issues",
"keywords": [
"embark",
"web3",
"node",
"ethereum",
"smart-contract"
],
"license": "MIT",
"repository": {
"directory": "packages/embarkjs-connector-web3",
"type": "git",
"url": "https://github.com/embark-framework/embark.git"
},
"main": "index.js",
"files": [
"embarkJSConnectorWeb3.js"
],
"scripts": {
"ci": "npm run qa",
"clean": "npm run reset",
"package": "npm pack",
"qa": "npm run package",
"reset": "npx rimraf embark-*.tgz package"
},
"dependencies": {
"embark-utils": "^4.1.0-beta.2",
"embark-i18n": "^4.1.0-beta.2"
},
"devDependencies": {
"rimraf": "2.6.3"
}
}

View File

@ -51,24 +51,6 @@ Blockchain.connect = function(options, callback) {
return connect(options);
};
Blockchain.connectConsole = function(doneCb) {
this.doFirst((cb) => {
this.blockchainConnector.getAccounts(async (err, accounts) => {
if (accounts) {
this.blockchainConnector.setDefaultAccount(accounts[0]);
}
let _err = err;
try {
await cb(_err);
} catch (e) {
_err = e;
} finally {
doneCb(_err);
}
});
});
};
Blockchain.doFirst = function(todo) {
todo((err) => {
this.done = true;