mirror of https://github.com/embarklabs/embark.git
Adding scaffold option to generate contract from scratch
This commit is contained in:
parent
b815ea4d44
commit
d6de374ce7
18
cmd/cmd.js
18
cmd/cmd.js
|
@ -326,19 +326,29 @@ class Cmd {
|
|||
|
||||
scaffold() {
|
||||
program
|
||||
.command('scaffold [contract] [environment]')
|
||||
.command('scaffold [contract] [fields...]')
|
||||
.option('--framework <framework>', 'UI framework to use. (default: react)')
|
||||
.option('--contract-language <language>', 'Language used for the smart contract generation (default: solidity)')
|
||||
.option('--overwrite', 'Overwrite existing files. (default: false)')
|
||||
|
||||
.action(function(contract, env, options){
|
||||
.action(function(contract, fields, options){
|
||||
if(contract === undefined){
|
||||
console.log("contract name is required");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const fieldMapping = {};
|
||||
if(fields.length > 0){
|
||||
// TODO: validate fields
|
||||
fields.forEach(curr => {
|
||||
const c = curr.split(':');
|
||||
fieldMapping[c[0]] = c[1];
|
||||
});
|
||||
}
|
||||
|
||||
checkDeps();
|
||||
i18n.setOrDetectLocale(options.locale);
|
||||
options.env = env || 'development';
|
||||
options.env = 'development';
|
||||
options.logFile = options.logfile; // fix casing
|
||||
options.logLevel = options.loglevel; // fix casing
|
||||
options.onlyCompile = options.contracts;
|
||||
|
@ -346,7 +356,9 @@ class Cmd {
|
|||
options.webpackConfigName = options.pipeline || 'development';
|
||||
options.contract = contract;
|
||||
options.framework = options.framework || 'react';
|
||||
options.contractLanguage = options.contractLanguage || 'solidity';
|
||||
options.overwrite = options.overwrite || false;
|
||||
options.fields = fieldMapping;
|
||||
|
||||
embark.scaffold(options);
|
||||
});
|
||||
|
|
|
@ -437,51 +437,68 @@ class EmbarkController {
|
|||
}
|
||||
|
||||
scaffold(options) {
|
||||
|
||||
this.context = options.context || [constants.contexts.scaffold];
|
||||
options.onlyCompile = true;
|
||||
|
||||
const Engine = require('../lib/core/engine.js');
|
||||
const engine = new Engine({
|
||||
env: options.env,
|
||||
client: options.client,
|
||||
locale: options.locale,
|
||||
version: this.version,
|
||||
embarkConfig: options.embarkConfig || 'embark.json',
|
||||
embarkConfig: 'embark.json',
|
||||
interceptLogs: false,
|
||||
logFile: options.logFile,
|
||||
context: this.context
|
||||
logLevel: options.logLevel,
|
||||
events: options.events,
|
||||
logger: options.logger,
|
||||
config: options.config,
|
||||
plugins: options.plugins,
|
||||
context: this.context,
|
||||
webpackConfigName: options.webpackConfigName
|
||||
});
|
||||
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
function initEngine(callback) {
|
||||
engine.init({}, callback);
|
||||
},
|
||||
function (callback) {
|
||||
function startServices(callback) {
|
||||
engine.startService("scaffolding");
|
||||
callback();
|
||||
},
|
||||
function generateContract(callback){
|
||||
engine.events.request('scaffolding:generate:contract', options, function(){
|
||||
// Engine is re-initiated to be able to see the new contract file
|
||||
engine.init({}, callback);
|
||||
});
|
||||
},
|
||||
function initEngineServices(callback){
|
||||
let pluginList = engine.plugins.listPlugins();
|
||||
if (pluginList.length > 0) {
|
||||
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
|
||||
}
|
||||
|
||||
engine.startService("processManager");
|
||||
engine.startService("serviceMonitor");
|
||||
engine.startService("libraryManager");
|
||||
engine.startService("pipeline");
|
||||
engine.startService("deployment", {onlyCompile: true});
|
||||
engine.startService("codeRunner");
|
||||
engine.startService("web3");
|
||||
engine.startService("scaffolding");
|
||||
|
||||
engine.events.request('deploy:contracts', callback);
|
||||
}
|
||||
], (err) => {
|
||||
if (err) {
|
||||
engine.logger.error(err.message);
|
||||
engine.logger.info(err.stack);
|
||||
} else {
|
||||
engine.startService("deployment", {onlyCompile: true});
|
||||
|
||||
callback();
|
||||
},
|
||||
function deploy(callback) {
|
||||
engine.events.request('deploy:contracts', function (err) {
|
||||
callback(err);
|
||||
});
|
||||
},
|
||||
function generateUI(callback){
|
||||
engine.events.request("scaffolding:generate", options, () => {
|
||||
engine.logger.info(__("finished generating the UI").underline);
|
||||
process.exit();
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
], function (_err) {
|
||||
engine.logger.info(__("finished generating the UI").underline);
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
upload(options) {
|
||||
|
|
|
@ -14,7 +14,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||
{{#if inputs.length}}
|
||||
input: {
|
||||
{{#each inputs}}
|
||||
{{name}}: {{#ifeq type 'bool'}}false{{else}}''{{/ifeq}}{{#unless @last}},{{/unless}}
|
||||
{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}: {{#ifeq type 'bool'}}false{{else}}''{{/ifeq}}{{#unless @last}},{{/unless}}
|
||||
{{/each}}
|
||||
},
|
||||
{{/if}}
|
||||
|
@ -50,7 +50,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||
|
||||
try {
|
||||
{{#ifview stateMutability}}
|
||||
const result = await {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{name}}{{#unless @last}}, {{/unless}}{{/each}}).call()
|
||||
const result = await {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}{{#unless @last}}, {{/unless}}{{/each}}).call()
|
||||
{{#iflengthgt outputs 1}}
|
||||
this.setState({output: {
|
||||
{{#each outputs}}
|
||||
|
@ -61,7 +61,7 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||
this.setState({output: result});
|
||||
{{/iflengthgt}}
|
||||
{{else}}
|
||||
const toSend = {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{name}}{{#unless @last}}, {{/unless}}{{/each}});
|
||||
const toSend = {{../contractName}}.methods{{methodname ../functions name inputs}}({{#each inputs}}input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}{{#unless @last}}, {{/unless}}{{/each}});
|
||||
|
||||
const estimatedGas = await toSend.estimateGas({from: web3.eth.defaultAccount});
|
||||
|
||||
|
@ -92,17 +92,17 @@ class {{capitalize name}}Form{{@index}} extends Component {
|
|||
{{#if inputs.length}}
|
||||
{{#each inputs}}
|
||||
<FormGroup>
|
||||
<ControlLabel>{{name}}</ControlLabel>
|
||||
<ControlLabel>{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}</ControlLabel>
|
||||
{{#ifeq type 'bool'}}
|
||||
<Checkbox
|
||||
onClick={(e) => this.handleCheckbox(e, '{{name}}')}
|
||||
onClick={(e) => this.handleCheckbox(e, '{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}')}
|
||||
/>
|
||||
{{else}}
|
||||
<FormControl
|
||||
type="text"
|
||||
defaultValue={ input.{{name}} }
|
||||
defaultValue={ input.{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}} }
|
||||
placeholder="{{type}}"
|
||||
onChange={(e) => this.handleChange(e, '{{name}}')}
|
||||
onChange={(e) => this.handleChange(e, '{{#ifeq name ''}}field{{else}}{{name}}{{/ifeq}}')}
|
||||
/>
|
||||
{{/ifeq}}
|
||||
</FormGroup>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
const Handlebars = require('handlebars');
|
||||
const fs = require('../../core/fs');
|
||||
|
||||
class ScaffoldingSolidity {
|
||||
constructor(embark, options){
|
||||
this.embark = embark;
|
||||
this.options = options;
|
||||
this.embark.registerDappGenerator('solidity', this.build.bind(this));
|
||||
}
|
||||
|
||||
_generateFile(contract, templateFilename, extension, data, overwrite){
|
||||
const filename = contract.className.toLowerCase() + '.' + extension;
|
||||
const filePath = './contracts/' + filename;
|
||||
if (!overwrite && fs.existsSync(filePath)){
|
||||
throw new Error("file '" + filePath + "' already exists");
|
||||
}
|
||||
|
||||
const templatePath = fs.embarkPath('lib/modules/scaffolding-solidity/templates/' + templateFilename);
|
||||
const source = fs.readFileSync(templatePath).toString();
|
||||
const template = Handlebars.compile(source);
|
||||
|
||||
// Write template
|
||||
const result = template(data);
|
||||
fs.writeFileSync(filePath, result);
|
||||
}
|
||||
|
||||
build(contractDetails, overwrite, cb){
|
||||
const {contract, fields} = contractDetails;
|
||||
try {
|
||||
const filename = contract.className.toLowerCase();
|
||||
|
||||
this._generateFile(contract, 'contract.sol.tpl', 'sol',
|
||||
{
|
||||
'contractName': contract.className,
|
||||
'structName': contract.className + "Struct",
|
||||
'fields': Object.keys(fields).map(f => { return {name:f, type:fields[f]}; })
|
||||
}, overwrite);
|
||||
this.embark.logger.info("contracts/" + filename + ".sol generated");
|
||||
|
||||
cb();
|
||||
|
||||
} catch(error){
|
||||
this.embark.logger.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ScaffoldingSolidity;
|
|
@ -0,0 +1,45 @@
|
|||
pragma solidity ^0.4.24;
|
||||
|
||||
contract {{contractName}} {
|
||||
|
||||
struct {{structName}} {
|
||||
{{#each fields}}
|
||||
{{type}} {{name}};
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
{{structName}}[] public items;
|
||||
|
||||
event ItemCreated(uint id, address createdBy);
|
||||
event ItemDeleted(uint id, address deletedBy);
|
||||
event ItemUpdated(uint id, address updatedBy);
|
||||
|
||||
function add({{#each fields}}{{type}} _{{name}}{{#unless @last}}, {{/unless}}{{/each}}) public {
|
||||
uint id = items.length++;
|
||||
items[id] = {{structName}}({
|
||||
{{#each fields}}
|
||||
{{name}}: _{{name}}{{#unless @last}},{{/unless}}
|
||||
{{/each}}
|
||||
});
|
||||
|
||||
emit ItemCreated(id, msg.sender);
|
||||
}
|
||||
|
||||
function edit(uint _id, {{#each fields}}{{type}} _{{name}}{{#unless @last}}, {{/unless}}{{/each}}) public {
|
||||
require(_id < items.length, "Invalid {{structName}} id");
|
||||
|
||||
{{#each fields}}
|
||||
items[_id].{{name}} = _{{name}};
|
||||
{{/each}}
|
||||
|
||||
emit ItemUpdated(_id, msg.sender);
|
||||
}
|
||||
|
||||
function remove(uint _id) public {
|
||||
require(_id < items.length, "Invalid {{structName}} id");
|
||||
|
||||
delete items[_id];
|
||||
emit ItemDeleted(_id, msg.sender);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,14 @@ class Scaffolding {
|
|||
|
||||
engine.events.setCommandHandler("scaffolding:generate", (options, cb) => {
|
||||
this.framework = options.framework;
|
||||
this.generate(options.contract, options.overwrite, cb);
|
||||
this.fields = options.fields;
|
||||
this.generate(options.contract, options.overwrite, false, cb);
|
||||
});
|
||||
|
||||
engine.events.setCommandHandler("scaffolding:generate:contract", (options, cb) => {
|
||||
this.framework = options.contractLanguage;
|
||||
this.fields = options.fields;
|
||||
this.generate(options.contract, options.overwrite, true, cb);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,32 +21,47 @@ class Scaffolding {
|
|||
return this.engine.config.contractsConfig.contracts[contractName] !== undefined;
|
||||
}
|
||||
|
||||
generate(contractName, overwrite, cb){
|
||||
if(this.framework === 'react'){
|
||||
this.plugins.loadInternalPlugin('scaffolding-react', this.options);
|
||||
}
|
||||
|
||||
getScaffoldPlugin(framework){
|
||||
let dappGenerators = this.plugins.getPluginsFor('dappGenerator');
|
||||
|
||||
let build = null;
|
||||
let builder;
|
||||
dappGenerators.forEach((plugin) => {
|
||||
plugin.dappGenerators.forEach((d) => {
|
||||
if(d.framework === this.framework){
|
||||
build = d.cb;
|
||||
if(d.framework === framework){
|
||||
builder = d.cb;
|
||||
}
|
||||
});
|
||||
});
|
||||
return builder;
|
||||
}
|
||||
|
||||
if(build === null){
|
||||
generate(contractName, overwrite, preDeployment, cb){
|
||||
|
||||
switch(this.framework){
|
||||
case 'react':
|
||||
this.plugins.loadInternalPlugin('scaffolding-react', this.options);
|
||||
break;
|
||||
case 'solidity':
|
||||
this.plugins.loadInternalPlugin('scaffolding-solidity', this.options);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
const fields = this.fields;
|
||||
|
||||
let build = this.getScaffoldPlugin(this.framework);
|
||||
if(!build){
|
||||
this.engine.logger.error("Could not find plugin for framework '" + this.framework + "'");
|
||||
process.exit();
|
||||
cb();
|
||||
} else if(!this.isContract(contractName)){
|
||||
} else if(!this.isContract(contractName) && Object.getOwnPropertyNames(this.fields).length === 0){
|
||||
this.engine.logger.error("contract '" + contractName + "' does not exist");
|
||||
cb();
|
||||
} else if(preDeployment) {
|
||||
build({contract: {className: contractName}, fields}, overwrite, cb);
|
||||
} else {
|
||||
// Contract exists
|
||||
this.engine.events.request("contracts:list", (_err, contractsList) => {
|
||||
if(_err) throw new Error(_err);
|
||||
|
||||
const contract = contractsList.find(x => x.className === contractName);
|
||||
try {
|
||||
build(contract, overwrite, cb);
|
||||
|
|
Loading…
Reference in New Issue