mirror of https://github.com/embarklabs/embark.git
feat(@embark/compiler): support :before and :after hooks on event compiler:contracts:compile (#1878)
* feat(@embark/compiler): support :before and :after hooks on event compiler:contracts:compile * style: keep root package.json sorted cleanly * style: formatting with prettier where print-width is 200 Per the suggestion of @iurimatias * WIP: PR review * WIP: PR review * WIP: refactor to check if file.path is within dappPath() or os.tmpdir() * WIP: PR review * WIP: use native async functions (and related JS lang constructs) instead of the async library * WIP: async.eachObject is no longer a dependency
This commit is contained in:
parent
5faa07cc9f
commit
043ccc0a24
16
package.json
16
package.json
|
@ -75,19 +75,19 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
"dapps/templates/*",
|
"dapps/templates/*",
|
||||||
"dapps/tests/*",
|
"dapps/tests/*",
|
||||||
"packages/core/*",
|
"packages/*",
|
||||||
"packages/plugins/*",
|
|
||||||
"packages/stack/*",
|
|
||||||
"packages/embarkjs/*",
|
|
||||||
"packages/cockpit/*",
|
"packages/cockpit/*",
|
||||||
"packages/*"
|
"packages/core/*",
|
||||||
|
"packages/embarkjs/*",
|
||||||
|
"packages/plugins/*",
|
||||||
|
"packages/stack/*"
|
||||||
],
|
],
|
||||||
"nohoist": [
|
"nohoist": [
|
||||||
"embark/embark-test-contract-0",
|
|
||||||
"embark/embark-test-contract-1",
|
|
||||||
"embark-dapp-template-demo/bootstrap",
|
"embark-dapp-template-demo/bootstrap",
|
||||||
"embark-dapp-test-app/embark-dapp-test-service",
|
"embark-dapp-test-app/embark-dapp-test-service",
|
||||||
"embark-dapp-test-app/zeppelin-solidity"
|
"embark-dapp-test-app/zeppelin-solidity",
|
||||||
|
"embark/embark-test-contract-0",
|
||||||
|
"embark/embark-test-contract-1"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {Callback} from "./callbacks";
|
||||||
|
|
||||||
export interface Plugin {
|
export interface Plugin {
|
||||||
dappGenerators: any;
|
dappGenerators: any;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +9,7 @@ export interface Plugins {
|
||||||
loadInternalPlugin(name: string, options: any): void;
|
loadInternalPlugin(name: string, options: any): void;
|
||||||
getPluginsProperty(pluginType: string, property: string, sub_property?: string): any[];
|
getPluginsProperty(pluginType: string, property: string, sub_property?: string): any[];
|
||||||
plugins: Plugin[];
|
plugins: Plugin[];
|
||||||
|
runActionsForEvent(event: string, args: any, cb: Callback<any>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Plugin {
|
export interface Plugin {
|
||||||
|
|
|
@ -62,5 +62,3 @@ arguments:
|
||||||
* `callback(error, compiledObject)`
|
* `callback(error, compiledObject)`
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
* async.eachObject
|
|
||||||
|
|
|
@ -41,9 +41,9 @@
|
||||||
"watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch"
|
"watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime-corejs2": "7.3.1",
|
"@babel/runtime-corejs2": "7.6.0",
|
||||||
"embark-async-wrapper": "^4.1.1",
|
"embark-i18n": "^4.1.1",
|
||||||
"embark-i18n": "^4.1.1"
|
"embark-utils": "^4.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.2.3",
|
"@babel/cli": "7.2.3",
|
||||||
|
|
|
@ -1,88 +1,130 @@
|
||||||
import {Callback, CompilerPluginObject, Embark, Plugins} /* supplied by @types/embark in packages/embark-typings */ from "embark";
|
import { Callback, CompilerPluginObject, Embark, Plugins /* supplied by @types/embark in packages/embark-typings */ } from "embark";
|
||||||
import { __ } from "embark-i18n";
|
import { __ } from "embark-i18n";
|
||||||
|
import * as os from "os";
|
||||||
|
import * as path from "path";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
const async = require("embark-async-wrapper");
|
const { File, Types, dappPath } = require("embark-utils");
|
||||||
|
|
||||||
class Compiler {
|
class Compiler {
|
||||||
|
private fs: any;
|
||||||
private logger: any;
|
private logger: any;
|
||||||
private plugins: Plugins;
|
private plugins: Plugins;
|
||||||
private isCoverage: boolean;
|
|
||||||
|
|
||||||
constructor(embark: Embark, options: any) {
|
constructor(embark: Embark, options: any) {
|
||||||
|
this.fs = embark.fs;
|
||||||
this.logger = embark.logger;
|
this.logger = embark.logger;
|
||||||
this.plugins = options.plugins;
|
this.plugins = options.plugins;
|
||||||
this.isCoverage = options.isCoverage;
|
|
||||||
|
|
||||||
// embark.events.setCommandHandler("compiler:contracts", this.compile_contracts.bind(this));
|
embark.events.setCommandHandler("compiler:contracts:compile", this.compileContracts.bind(this));
|
||||||
embark.events.setCommandHandler("compiler:contracts:compile", this.compile_contracts.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private compile_contracts(contractFiles: any[], cb: any) {
|
private async runAfterActions(compiledObject: any): Promise<any> {
|
||||||
if (contractFiles.length === 0) {
|
return (((await promisify(this.plugins.runActionsForEvent.bind(this.plugins, "compiler:contracts:compile:after"))(compiledObject)) as unknown) as any) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runBeforeActions(contractFiles: any[]): Promise<any[]> {
|
||||||
|
return (((await promisify(this.plugins.runActionsForEvent.bind(this.plugins, "compiler:contracts:compile:before"))(contractFiles)) as unknown) as any[]) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private *callCompilers(compilers: any, matchingFiles: any[], compilerOptions: object) {
|
||||||
|
for (const compiler of compilers) {
|
||||||
|
yield promisify(compiler)(matchingFiles, compilerOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkContractFiles(contractFiles: any[]) {
|
||||||
|
const _dappPath = dappPath();
|
||||||
|
const osTmpDir = os.tmpdir();
|
||||||
|
|
||||||
|
return contractFiles.map((file) => {
|
||||||
|
if (file instanceof File) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.path) {
|
||||||
|
throw new TypeError("path property was missing on contract file object");
|
||||||
|
}
|
||||||
|
|
||||||
|
let filePath: string;
|
||||||
|
if (!path.isAbsolute(file.path)) {
|
||||||
|
filePath = dappPath(file.path);
|
||||||
|
} else {
|
||||||
|
filePath = path.normalize(file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![_dappPath, osTmpDir].some((dir) => filePath.startsWith(dir))) {
|
||||||
|
throw new Error("path must be within the DApp project or the OS temp directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File({
|
||||||
|
originalPath: file.path,
|
||||||
|
path: file.path,
|
||||||
|
resolver: (callback: Callback<any>) => {
|
||||||
|
this.fs.readFile(file.path, { encoding: "utf-8" }, callback);
|
||||||
|
},
|
||||||
|
type: Types.dappFile,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async compileContracts(contractFiles: any[], cb: Callback<any>) {
|
||||||
|
if (!contractFiles.length) {
|
||||||
return cb(null, {});
|
return cb(null, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
const compiledObject: { [index: string]: any } = {};
|
const compiledObject: { [index: string]: any } = {};
|
||||||
|
const compilerOptions = {};
|
||||||
|
|
||||||
const compilerOptions = {
|
try {
|
||||||
isCoverage: this.isCoverage,
|
contractFiles = this.checkContractFiles(await this.runBeforeActions(contractFiles));
|
||||||
};
|
|
||||||
|
|
||||||
async.eachObject(this.getAvailableCompilers(),
|
await Promise.all(
|
||||||
(extension: string, compilers: any, next: any) => {
|
Object.entries(this.getAvailableCompilers()).map(async ([extension, compilers]: [string, any]) => {
|
||||||
const matchingFiles = contractFiles.filter(this.filesMatchingExtension(extension));
|
const matchingFiles = contractFiles.filter(this.filesMatchingExtension(extension));
|
||||||
if (matchingFiles.length === 0) {
|
if (!matchingFiles.length) {
|
||||||
return next();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async.someLimit(compilers, 1, (compiler: any, someCb: Callback<boolean>) => {
|
for await (const compileResult of this.callCompilers(compilers, matchingFiles, compilerOptions)) {
|
||||||
compiler.call(compiler, matchingFiles, compilerOptions, (err: any, compileResult: any) => {
|
|
||||||
if (err) {
|
|
||||||
return someCb(err);
|
|
||||||
}
|
|
||||||
if (compileResult === false) {
|
if (compileResult === false) {
|
||||||
// Compiler not compatible, trying the next one
|
continue;
|
||||||
return someCb(null, false);
|
|
||||||
}
|
}
|
||||||
Object.assign(compiledObject, compileResult);
|
Object.assign(compiledObject, compileResult);
|
||||||
someCb(null, true);
|
return;
|
||||||
});
|
|
||||||
}, (err: Error, result: boolean) => {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
if (!result) {
|
|
||||||
// No compiler was compatible
|
throw new Error(__("No installed compiler was compatible with your version of %s files", extension));
|
||||||
return next(new Error(__("No installed compiler was compatible with your version of %s files", extension)));
|
}),
|
||||||
}
|
);
|
||||||
next();
|
|
||||||
});
|
contractFiles
|
||||||
},
|
.filter((f: any) => !f.compiled)
|
||||||
(err: any) => {
|
.forEach((file: any) => {
|
||||||
contractFiles.filter((f: any) => !f.compiled).forEach((file: any) => {
|
|
||||||
this.logger.warn(__("%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.", file.path));
|
this.logger.warn(__("%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.", file.path));
|
||||||
});
|
});
|
||||||
|
|
||||||
cb(err, compiledObject);
|
cb(null, await this.runAfterActions(compiledObject));
|
||||||
},
|
} catch (err) {
|
||||||
);
|
cb(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAvailableCompilers() {
|
private getAvailableCompilers() {
|
||||||
const available_compilers: { [index: string]: any } = {};
|
const availableCompilers: { [index: string]: any } = {};
|
||||||
this.plugins.getPluginsProperty("compilers", "compilers").forEach((compilerObject: CompilerPluginObject) => {
|
this.plugins.getPluginsProperty("compilers", "compilers").forEach((compilerObject: CompilerPluginObject) => {
|
||||||
if (!available_compilers[compilerObject.extension]) {
|
if (!availableCompilers[compilerObject.extension]) {
|
||||||
available_compilers[compilerObject.extension] = [];
|
availableCompilers[compilerObject.extension] = [];
|
||||||
}
|
}
|
||||||
available_compilers[compilerObject.extension].unshift(compilerObject.cb);
|
availableCompilers[compilerObject.extension].unshift(compilerObject.cb);
|
||||||
});
|
});
|
||||||
return available_compilers;
|
return availableCompilers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private filesMatchingExtension(extension: string) {
|
private filesMatchingExtension(extension: string) {
|
||||||
return (file: any) => {
|
return (file: any) => {
|
||||||
const fileMatch = file.path.match(/\.[0-9a-z]+$/);
|
const fileMatch = file.path.match(/\.[0-9a-z]+$/);
|
||||||
if (fileMatch && (fileMatch[0] === extension)) {
|
if (fileMatch && fileMatch[0] === extension) {
|
||||||
file.compiled = true;
|
file.compiled = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1047,6 +1047,14 @@
|
||||||
core-js "^2.5.7"
|
core-js "^2.5.7"
|
||||||
regenerator-runtime "^0.12.0"
|
regenerator-runtime "^0.12.0"
|
||||||
|
|
||||||
|
"@babel/runtime-corejs2@7.6.0":
|
||||||
|
version "7.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.6.0.tgz#6fcd37c2580070817d62f219db97f67e26f50f9c"
|
||||||
|
integrity sha512-zbPQzlbyJab2xCYb6VaESn8Tk/XiVpQJU7WvIKiQCwlFyc2NSCzKjqtBXCvpZBbiTOHCx10s2656REVnySwb+A==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.6.5"
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/runtime-corejs2@^7.0.0":
|
"@babel/runtime-corejs2@^7.0.0":
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2"
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2"
|
||||||
|
|
Loading…
Reference in New Issue