Use ServiceWorker for Contract compilation
This commit is contained in:
parent
a74bce2c06
commit
365ab60fed
@ -117,6 +117,7 @@
|
|||||||
"react-hot-loader": "3.0.0-beta.6",
|
"react-hot-loader": "3.0.0-beta.6",
|
||||||
"react-intl-aggregate-webpack-plugin": "0.0.1",
|
"react-intl-aggregate-webpack-plugin": "0.0.1",
|
||||||
"rucksack-css": "0.9.1",
|
"rucksack-css": "0.9.1",
|
||||||
|
"serviceworker-webpack-plugin": "0.1.7",
|
||||||
"sinon": "1.17.6",
|
"sinon": "1.17.6",
|
||||||
"sinon-as-promised": "4.0.2",
|
"sinon-as-promised": "4.0.2",
|
||||||
"sinon-chai": "2.8.0",
|
"sinon-chai": "2.8.0",
|
||||||
|
@ -14,7 +14,45 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import CompilerWorker from 'worker-loader!./compilerWorker.js';
|
import PromiseWorker from 'promise-worker';
|
||||||
|
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
|
||||||
|
|
||||||
|
let workerRegistration;
|
||||||
|
|
||||||
|
// Setup the Service Worker
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
workerRegistration = runtime
|
||||||
|
.register()
|
||||||
|
.then(() => {
|
||||||
|
console.log('registering service worker');
|
||||||
|
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// already active and controlling this page
|
||||||
|
return navigator.serviceWorker;
|
||||||
|
}
|
||||||
|
// wait for a new service worker to control this page
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const onControllerChange = () => {
|
||||||
|
navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
|
||||||
|
resolve(navigator.serviceWorker);
|
||||||
|
};
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((_worker) => {
|
||||||
|
const worker = new PromiseWorker(_worker);
|
||||||
|
|
||||||
|
console.log('registered service worker');
|
||||||
|
return worker;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
workerRegistration = Promise.reject('Service Worker is not available in your browser.');
|
||||||
|
}
|
||||||
|
|
||||||
export function setWorker (worker) {
|
export function setWorker (worker) {
|
||||||
return {
|
return {
|
||||||
@ -23,6 +61,13 @@ export function setWorker (worker) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setError (error) {
|
||||||
|
return {
|
||||||
|
type: 'setError',
|
||||||
|
error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function setupWorker () {
|
export function setupWorker () {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
@ -31,7 +76,13 @@ export function setupWorker () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const worker = new CompilerWorker();
|
workerRegistration
|
||||||
dispatch(setWorker(worker));
|
.then((worker) => {
|
||||||
|
dispatch(setWorker(worker));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('sw', error);
|
||||||
|
dispatch(setError(error));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,18 @@
|
|||||||
import { handleActions } from 'redux-actions';
|
import { handleActions } from 'redux-actions';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
worker: null
|
worker: null,
|
||||||
|
error: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions({
|
export default handleActions({
|
||||||
setWorker (state, action) {
|
setWorker (state, action) {
|
||||||
const { worker } = action;
|
const { worker } = action;
|
||||||
|
|
||||||
return Object.assign({}, state, { worker });
|
return Object.assign({}, state, { worker });
|
||||||
|
},
|
||||||
|
|
||||||
|
setError (state, action) {
|
||||||
|
const { error } = action;
|
||||||
|
return Object.assign({}, state, { error });
|
||||||
}
|
}
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
185
js/src/serviceWorker.js
Normal file
185
js/src/serviceWorker.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import solc from 'solc/browser-wrapper';
|
||||||
|
import { isWebUri } from 'valid-url';
|
||||||
|
import registerPromiseWorker from 'promise-worker/register';
|
||||||
|
|
||||||
|
const CACHE_NAME = 'parity-cache-v1';
|
||||||
|
|
||||||
|
registerPromiseWorker((msg) => {
|
||||||
|
return handleMessage(msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(self.skipWaiting());
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(self.clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
self.solcVersions = {};
|
||||||
|
self.files = {};
|
||||||
|
|
||||||
|
function handleMessage (message) {
|
||||||
|
switch (message.action) {
|
||||||
|
case 'compile':
|
||||||
|
return compile(message.data);
|
||||||
|
|
||||||
|
case 'load':
|
||||||
|
return load(message.data);
|
||||||
|
|
||||||
|
case 'setFiles':
|
||||||
|
return setFiles(message.data);
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`unknown action "${message.action}"`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFiles (files) {
|
||||||
|
const prevFiles = self.files;
|
||||||
|
const nextFiles = files.reduce((obj, file) => {
|
||||||
|
obj[file.name] = file.sourcecode;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
self.files = {
|
||||||
|
...prevFiles,
|
||||||
|
...nextFiles
|
||||||
|
};
|
||||||
|
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo re-implement find imports (with ASYNC fetch)
|
||||||
|
// function findImports (path) {
|
||||||
|
// if (self.files[path]) {
|
||||||
|
// if (self.files[path].error) {
|
||||||
|
// return Promise.reject(self.files[path].error);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return Promise.resolve(self.files[path]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (isWebUri(path)) {
|
||||||
|
// console.log('[sw] fetching', path);
|
||||||
|
|
||||||
|
// return fetch(path)
|
||||||
|
// .then((r) => r.text())
|
||||||
|
// .then((c) => {
|
||||||
|
// console.log('[sw]', 'got content at ' + path);
|
||||||
|
// self.files[path] = c;
|
||||||
|
// return c;
|
||||||
|
// })
|
||||||
|
// .catch((e) => {
|
||||||
|
// console.error('[sw]', 'fetching', path, e);
|
||||||
|
// self.files[path] = { error: e };
|
||||||
|
// throw e;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log(`[sw] path ${path} not found...`);
|
||||||
|
// return Promise.reject('File not found');
|
||||||
|
// }
|
||||||
|
|
||||||
|
function compile (data, optimized = 1) {
|
||||||
|
const { sourcecode, build } = data;
|
||||||
|
|
||||||
|
return fetchSolidity(build)
|
||||||
|
.then((compiler) => {
|
||||||
|
const start = Date.now();
|
||||||
|
console.log('[sw] compiling...');
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
'': sourcecode
|
||||||
|
};
|
||||||
|
|
||||||
|
const compiled = compiler.compile({ sources: input }, optimized);
|
||||||
|
|
||||||
|
const time = Math.round((Date.now() - start) / 100) / 10;
|
||||||
|
console.log(`[sw] done compiling in ${time}s`);
|
||||||
|
|
||||||
|
compiled.version = build.longVersion;
|
||||||
|
|
||||||
|
return compiled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function load (build) {
|
||||||
|
return fetchSolidity(build)
|
||||||
|
.then(() => 'ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSolc (build) {
|
||||||
|
const { path, longVersion } = build;
|
||||||
|
const URL = `https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/${path}`;
|
||||||
|
|
||||||
|
return caches
|
||||||
|
.match(URL)
|
||||||
|
.then((response) => {
|
||||||
|
if (response) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[sw] fetching solc-bin ${longVersion} at ${URL}`);
|
||||||
|
|
||||||
|
return fetch(URL)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseToCache = response.clone();
|
||||||
|
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then((cache) => {
|
||||||
|
cache.put(URL, responseToCache);
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSolidity (build) {
|
||||||
|
const { path, longVersion } = build;
|
||||||
|
|
||||||
|
if (self.solcVersions[path]) {
|
||||||
|
return Promise.resolve(self.solcVersions[path]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetchSolc(build)
|
||||||
|
.then((r) => r.text())
|
||||||
|
.then((code) => {
|
||||||
|
const solcCode = code.replace(/^var Module;/, 'var Module=self.__solcModule;');
|
||||||
|
self.__solcModule = {};
|
||||||
|
|
||||||
|
console.log(`[sw] evaluating ${longVersion}`);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
eval(solcCode);
|
||||||
|
|
||||||
|
console.log(`[sw] done evaluating ${longVersion}`);
|
||||||
|
|
||||||
|
const compiler = solc(self.__solcModule);
|
||||||
|
self.solcVersions[path] = compiler;
|
||||||
|
|
||||||
|
return compiler;
|
||||||
|
});
|
||||||
|
}
|
@ -45,6 +45,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: rgba(200, 0, 0, 0.25);
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
margin-top: -0.5em;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
.mainEditor {
|
.mainEditor {
|
||||||
&:global(.ace-solarized-dark) {
|
&:global(.ace-solarized-dark) {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
@ -87,13 +95,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compilation {
|
.compilation {
|
||||||
|
@ -42,10 +42,11 @@ class WriteContract extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
accounts: PropTypes.object.isRequired,
|
accounts: PropTypes.object.isRequired,
|
||||||
setupWorker: PropTypes.func.isRequired,
|
setupWorker: PropTypes.func.isRequired,
|
||||||
worker: PropTypes.object
|
worker: PropTypes.object,
|
||||||
|
workerError: PropTypes.any
|
||||||
};
|
};
|
||||||
|
|
||||||
store = new WriteContractStore();
|
store = WriteContractStore.get();
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
resizing: false,
|
resizing: false,
|
||||||
@ -57,22 +58,31 @@ class WriteContract extends Component {
|
|||||||
setupWorker();
|
setupWorker();
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
this.store.setCompiler(worker);
|
this.store.setWorker(worker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.store.setEditor(this.refs.editor);
|
this.store.setEditor(this.refs.editor);
|
||||||
|
|
||||||
|
if (this.props.workerError) {
|
||||||
|
this.store.setWorkerError(this.props.workerError);
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for editor to be loaded
|
// Wait for editor to be loaded
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
this.store.resizeEditor();
|
this.store.resizeEditor();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the worker if not set before (eg. first page loading)
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (!this.props.worker && nextProps.worker) {
|
if (!this.props.worker && nextProps.worker) {
|
||||||
this.store.setCompiler(nextProps.worker);
|
this.store.setWorker(nextProps.worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.workerError !== nextProps.workerError) {
|
||||||
|
this.store.setWorkerError(nextProps.workerError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +227,18 @@ class WriteContract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderParameters () {
|
renderParameters () {
|
||||||
const { compiling, contract, selectedBuild, loading } = this.store;
|
const { compiling, contract, selectedBuild, loading, workerError } = this.store;
|
||||||
|
|
||||||
|
if (workerError) {
|
||||||
|
return (
|
||||||
|
<div className={ styles.panel }>
|
||||||
|
<div className={ styles.centeredMessage }>
|
||||||
|
<p>Unfortuantely, an error occurred...</p>
|
||||||
|
<div className={ styles.error }>{ workerError }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedBuild < 0) {
|
if (selectedBuild < 0) {
|
||||||
return (
|
return (
|
||||||
@ -485,8 +506,8 @@ class WriteContract extends Component {
|
|||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { accounts } = state.personal;
|
const { accounts } = state.personal;
|
||||||
const { worker } = state.compiler;
|
const { worker, error } = state.compiler;
|
||||||
return { accounts, worker };
|
return { accounts, worker, workerError: error };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
|
@ -18,6 +18,8 @@ import { action, observable } from 'mobx';
|
|||||||
import store from 'store';
|
import store from 'store';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
import { sha3 } from '~/api/util/sha3';
|
||||||
|
|
||||||
const WRITE_CONTRACT_STORE_KEY = '_parity::writeContractStore';
|
const WRITE_CONTRACT_STORE_KEY = '_parity::writeContractStore';
|
||||||
|
|
||||||
const SNIPPETS = {
|
const SNIPPETS = {
|
||||||
@ -43,6 +45,8 @@ const SNIPPETS = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
export default class WriteContractStore {
|
export default class WriteContractStore {
|
||||||
|
|
||||||
@observable sourcecode = '';
|
@observable sourcecode = '';
|
||||||
@ -68,45 +72,47 @@ export default class WriteContractStore {
|
|||||||
@observable savedContracts = {};
|
@observable savedContracts = {};
|
||||||
@observable selectedContract = {};
|
@observable selectedContract = {};
|
||||||
|
|
||||||
|
@observable workerError = null;
|
||||||
|
|
||||||
|
lastCompilation = {};
|
||||||
snippets = SNIPPETS;
|
snippets = SNIPPETS;
|
||||||
|
worker = null;
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this.reloadContracts();
|
|
||||||
this.fetchSolidityVersions();
|
|
||||||
|
|
||||||
this.debouncedCompile = debounce(this.handleCompile, 1000);
|
this.debouncedCompile = debounce(this.handleCompile, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get () {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new WriteContractStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setWorkerError (error) {
|
||||||
|
this.workerError = error;
|
||||||
|
}
|
||||||
|
|
||||||
@action setEditor (editor) {
|
@action setEditor (editor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action setCompiler (compiler) {
|
@action setWorker (worker) {
|
||||||
this.compiler = compiler;
|
this.worker = worker;
|
||||||
|
|
||||||
this.compiler.onmessage = (event) => {
|
this
|
||||||
const message = JSON.parse(event.data);
|
.fetchSolidityVersions()
|
||||||
|
.then(() => this.reloadContracts());
|
||||||
switch (message.event) {
|
|
||||||
case 'compiled':
|
|
||||||
this.parseCompiled(message.data);
|
|
||||||
break;
|
|
||||||
case 'loading':
|
|
||||||
this.parseLoading(message.data);
|
|
||||||
break;
|
|
||||||
case 'try-again':
|
|
||||||
this.handleCompile();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSolidityVersions () {
|
fetchSolidityVersions () {
|
||||||
fetch('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.json')
|
return fetch('https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.json')
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const { builds, releases, latestRelease } = data;
|
const { builds, releases, latestRelease } = data;
|
||||||
let latestIndex = -1;
|
let latestIndex = -1;
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
|
||||||
this.builds = builds.reverse().map((build, index) => {
|
this.builds = builds.reverse().map((build, index) => {
|
||||||
if (releases[build.version] === build.path) {
|
if (releases[build.version] === build.path) {
|
||||||
@ -114,7 +120,7 @@ export default class WriteContractStore {
|
|||||||
|
|
||||||
if (build.version === latestRelease) {
|
if (build.version === latestRelease) {
|
||||||
build.latest = true;
|
build.latest = true;
|
||||||
this.loadSolidityVersion(build);
|
promise = promise.then(() => this.loadSolidityVersion(build));
|
||||||
latestIndex = index;
|
latestIndex = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,29 +129,40 @@ export default class WriteContractStore {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.selectedBuild = latestIndex;
|
this.selectedBuild = latestIndex;
|
||||||
|
return promise;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action closeWorker = () => {
|
|
||||||
this.compiler.postMessage(JSON.stringify({
|
|
||||||
action: 'close'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
@action handleImport = (sourcecode) => {
|
@action handleImport = (sourcecode) => {
|
||||||
this.reloadContracts(-1, sourcecode);
|
this.reloadContracts(-1, sourcecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action handleSelectBuild = (_, index, value) => {
|
@action handleSelectBuild = (_, index, value) => {
|
||||||
this.selectedBuild = value;
|
this.selectedBuild = value;
|
||||||
this.loadSolidityVersion(this.builds[value]);
|
return this.loadSolidityVersion(this.builds[value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action loadSolidityVersion = (build) => {
|
@action loadSolidityVersion = (build) => {
|
||||||
this.compiler.postMessage(JSON.stringify({
|
if (!this.worker) {
|
||||||
action: 'load',
|
return;
|
||||||
data: build
|
}
|
||||||
}));
|
|
||||||
|
return this.worker
|
||||||
|
.postMessage({
|
||||||
|
action: 'load',
|
||||||
|
data: build
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result !== 'ok') {
|
||||||
|
this.setWorkerError(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setWorkerError(error);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action handleOpenDeployModal = () => {
|
@action handleOpenDeployModal = () => {
|
||||||
@ -177,23 +194,94 @@ export default class WriteContractStore {
|
|||||||
this.contract = this.contracts[Object.keys(this.contracts)[value]];
|
this.contract = this.contracts[Object.keys(this.contracts)[value]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@action handleCompile = () => {
|
@action handleCompile = (loadFiles = false) => {
|
||||||
this.compiled = false;
|
this.compiled = false;
|
||||||
this.compiling = true;
|
this.compiling = true;
|
||||||
|
|
||||||
const build = this.builds[this.selectedBuild];
|
const build = this.builds[this.selectedBuild];
|
||||||
|
const version = build.longVersion;
|
||||||
|
const sourcecode = this.sourcecode.replace(/\n+/g, '\n').replace(/\s(\s+)/g, ' ');
|
||||||
|
const hash = sha3(JSON.stringify({ version, sourcecode }));
|
||||||
|
|
||||||
if (this.compiler && typeof this.compiler.postMessage === 'function') {
|
let promise = Promise.resolve(null);
|
||||||
this.sendFilesToWorker();
|
|
||||||
|
|
||||||
this.compiler.postMessage(JSON.stringify({
|
if (hash === this.lastCompilation.hash) {
|
||||||
action: 'compile',
|
promise = new Promise((resolve) => {
|
||||||
data: {
|
window.setTimeout(() => {
|
||||||
sourcecode: this.sourcecode,
|
resolve(this.lastCompilation);
|
||||||
build: build
|
}, 500);
|
||||||
}
|
});
|
||||||
}));
|
} else if (this.worker) {
|
||||||
|
promise = loadFiles
|
||||||
|
? this.sendFilesToWorker()
|
||||||
|
: Promise.resolve();
|
||||||
|
|
||||||
|
promise = promise
|
||||||
|
.then(() => {
|
||||||
|
return this.worker.postMessage({
|
||||||
|
action: 'compile',
|
||||||
|
data: {
|
||||||
|
sourcecode: sourcecode,
|
||||||
|
build: build
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
const result = this.parseCompiled(data);
|
||||||
|
|
||||||
|
this.lastCompilation = {
|
||||||
|
result: result,
|
||||||
|
date: new Date(),
|
||||||
|
version: data.version,
|
||||||
|
hash
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.lastCompilation;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setWorkerError(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return promise.then((data = {}) => {
|
||||||
|
const {
|
||||||
|
contract, contractIndex,
|
||||||
|
annotations, contracts, errors
|
||||||
|
} = data.result;
|
||||||
|
|
||||||
|
this.contract = contract;
|
||||||
|
this.contractIndex = contractIndex;
|
||||||
|
|
||||||
|
this.annotations = annotations;
|
||||||
|
this.contracts = contracts;
|
||||||
|
this.errors = errors;
|
||||||
|
|
||||||
|
this.compiled = true;
|
||||||
|
this.compiling = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action parseCompiled = (data) => {
|
||||||
|
const { contracts } = data;
|
||||||
|
|
||||||
|
const { errors = [] } = data;
|
||||||
|
const errorAnnotations = this.parseErrors(errors);
|
||||||
|
const formalAnnotations = this.parseErrors(data.formal && data.formal.errors, true);
|
||||||
|
|
||||||
|
const annotations = [].concat(
|
||||||
|
errorAnnotations,
|
||||||
|
formalAnnotations
|
||||||
|
);
|
||||||
|
|
||||||
|
const contractKeys = Object.keys(contracts || {});
|
||||||
|
|
||||||
|
const contract = contractKeys.length ? contracts[contractKeys[0]] : null;
|
||||||
|
const contractIndex = contractKeys.length ? 0 : -1;
|
||||||
|
|
||||||
|
return {
|
||||||
|
contract, contractIndex,
|
||||||
|
contracts, errors, annotations
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
parseErrors = (data, formal = false) => {
|
parseErrors = (data, formal = false) => {
|
||||||
@ -220,43 +308,6 @@ export default class WriteContractStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action parseCompiled = (data) => {
|
|
||||||
const { contracts } = data;
|
|
||||||
|
|
||||||
const { errors = [] } = data;
|
|
||||||
const errorAnnotations = this.parseErrors(errors);
|
|
||||||
const formalAnnotations = this.parseErrors(data.formal && data.formal.errors, true);
|
|
||||||
|
|
||||||
const annotations = [].concat(
|
|
||||||
errorAnnotations,
|
|
||||||
formalAnnotations
|
|
||||||
);
|
|
||||||
|
|
||||||
if (annotations.findIndex((a) => /__parity_tryAgain/.test(a.text)) > -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const contractKeys = Object.keys(contracts || {});
|
|
||||||
|
|
||||||
this.contract = contractKeys.length ? contracts[contractKeys[0]] : null;
|
|
||||||
this.contractIndex = contractKeys.length ? 0 : -1;
|
|
||||||
|
|
||||||
this.contracts = contracts;
|
|
||||||
this.errors = errors;
|
|
||||||
this.annotations = annotations;
|
|
||||||
|
|
||||||
this.compiled = true;
|
|
||||||
this.compiling = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action parseLoading = (isLoading) => {
|
|
||||||
this.loading = isLoading;
|
|
||||||
|
|
||||||
if (!isLoading) {
|
|
||||||
this.handleCompile();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action handleEditSourcecode = (value, compile = false) => {
|
@action handleEditSourcecode = (value, compile = false) => {
|
||||||
this.sourcecode = value;
|
this.sourcecode = value;
|
||||||
|
|
||||||
@ -327,8 +378,10 @@ export default class WriteContractStore {
|
|||||||
current: this.sourcecode
|
current: this.sourcecode
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleCompile();
|
|
||||||
this.resizeEditor();
|
this.resizeEditor();
|
||||||
|
|
||||||
|
// Send the new files to the Worker and compile
|
||||||
|
return this.handleCompile(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action handleLoadContract = (contract) => {
|
@action handleLoadContract = (contract) => {
|
||||||
@ -369,10 +422,10 @@ export default class WriteContractStore {
|
|||||||
Object.values(this.savedContracts)
|
Object.values(this.savedContracts)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.compiler.postMessage(JSON.stringify({
|
return this.worker.postMessage({
|
||||||
action: 'setFiles',
|
action: 'setFiles',
|
||||||
data: files
|
data: files
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ const WebpackErrorNotificationPlugin = require('webpack-error-notification');
|
|||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
||||||
|
|
||||||
const Shared = require('./shared');
|
const Shared = require('./shared');
|
||||||
const DAPPS = require('../src/dapps');
|
const DAPPS = require('../src/dapps');
|
||||||
@ -50,7 +51,7 @@ module.exports = {
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules/,
|
exclude: /(node_modules)/,
|
||||||
// use: [ 'happypack/loader?id=js' ]
|
// use: [ 'happypack/loader?id=js' ]
|
||||||
use: isProd ? ['babel-loader'] : [
|
use: isProd ? ['babel-loader'] : [
|
||||||
'babel-loader?cacheDirectory=true'
|
'babel-loader?cacheDirectory=true'
|
||||||
@ -136,7 +137,18 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
plugins: (function () {
|
plugins: (function () {
|
||||||
const plugins = Shared.getPlugins().concat([
|
const DappsHTMLInjection = DAPPS.map((dapp) => {
|
||||||
|
return new HtmlWebpackPlugin({
|
||||||
|
title: dapp.title,
|
||||||
|
filename: dapp.name + '.html',
|
||||||
|
template: './dapps/index.ejs',
|
||||||
|
favicon: FAVICON,
|
||||||
|
secure: dapp.secure,
|
||||||
|
chunks: [ isProd ? null : 'commons', dapp.name ]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const plugins = Shared.getPlugins().concat(
|
||||||
new CopyWebpackPlugin([{ from: './error_pages.css', to: 'styles.css' }], {}),
|
new CopyWebpackPlugin([{ from: './error_pages.css', to: 'styles.css' }], {}),
|
||||||
new WebpackErrorNotificationPlugin(),
|
new WebpackErrorNotificationPlugin(),
|
||||||
|
|
||||||
@ -151,17 +163,14 @@ module.exports = {
|
|||||||
template: './index.ejs',
|
template: './index.ejs',
|
||||||
favicon: FAVICON,
|
favicon: FAVICON,
|
||||||
chunks: [ isProd ? null : 'commons', 'index' ]
|
chunks: [ isProd ? null : 'commons', 'index' ]
|
||||||
})
|
}),
|
||||||
], DAPPS.map((dapp) => {
|
|
||||||
return new HtmlWebpackPlugin({
|
new ServiceWorkerWebpackPlugin({
|
||||||
title: dapp.title,
|
entry: path.join(__dirname, '../src/serviceWorker.js'),
|
||||||
filename: dapp.name + '.html',
|
}),
|
||||||
template: './dapps/index.ejs',
|
|
||||||
favicon: FAVICON,
|
DappsHTMLInjection
|
||||||
secure: dapp.secure,
|
);
|
||||||
chunks: [ isProd ? null : 'commons', dapp.name ]
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (!isProd) {
|
if (!isProd) {
|
||||||
const DEST_I18N = path.join(__dirname, '..', DEST, 'i18n');
|
const DEST_I18N = path.join(__dirname, '..', DEST, 'i18n');
|
||||||
|
Loading…
Reference in New Issue
Block a user