const path = require('path');
const fs = require('fs');
const solc = require('solc');
const util = require('util');
const ora = require('ora');
const solcutil = require('./solc-util');
const SolcUtil = new solcutil();
const Logger = require('../logging/logger');
/** Compiler class, abstracting solc. */
class Compiler {
/**
* Initialize a Compiler object.
* @param {object} options - User options
* @param {Logger.state.ENUM} logSetting - Log setting, as represented by the Logger state enum.
*/
constructor(options, logSetting = Logger.state.NORMAL) {
this.options = options;
this.log = new Logger(logSetting);
}
//solc input object --> Compiled stuff
/**
*
* @param {object} solcInput - The JSON input for the solc.js Solidity Compiler. See: https://solidity.readthedocs.io/en/v0.5.7/using-the-compiler.html#input-description
* @return {object} output - The returned output generated by solc. See link above!
*
*/
compile(solcInput) {
this.log.print(Logger.state.SUPER,
util.inspect(solcInput),
"\n"
);
//this.log.print(Logger.state.NORMAL, "Compiling...\n");
const spinner = ora('Compiling...\n').start();
const output = JSON.parse(solc.compile(JSON.stringify(solcInput)));
//Logic on what to show post-compilation (regarding the output post-compilation)
if(this.log.setting >= Logger.state.SUPER) {
this.log.print(Logger.state.SUPER,
"OUTPUT",
util.inspect(output, { depth: null })
);
} else if(output.errors) {
spinner.fail();
this.log.print(Logger.state.NORMAL,
"Error: ",
util.inspect(output, { depth: null })
);
throw "Failed to compile!";
}
spinner.stop();
return output;
}
/**
* A function which recursively searches through the given directory passed, pasgenerating a solc input, compiling it, and returning the raw compiler output.
*
* If no root filepath is passed, it will assume that the "contracts" folder one level higher than the current dir is the root where all contracts are located.
*
* @param {string} [root=path.resolve(__dirname, '../contracts')] - The root directory in which the function will start from, recursively navigated through to parse all contracts found into a solc input.
* @return {object} output - The returned output generated by solc. See: https://solidity.readthedocs.io/en/v0.5.7/using-the-compiler.html#output-description
*/
compileDirectory(root = path.resolve(__dirname, '../contracts')) {
this.log.print(Logger.state.MASTER,
"Config: ",
" Root contract directory: " + root,
"\n"
);
this.log.print(Logger.state.NORMAL, "Generating solc_input...\n");
return this.compile(SolcUtil.generateSolcInputDirectory(root));
}
/**
* A function which simply compiles a single file located at the given filepath.
*
* If no root is provided in the second argument, it will assume that the "contracts" folder one level higher than the current dir is the root where all contracts are located.
*
* @param {string} filepath - The filepath at which the file is located.
* @param {string} [root=path.resolve(__dirname, '../contracts')] - The root directory in which all contracts are located; this is for contract naming specification within the solc input.
* @return {object} output - The returned output generated by solc. See: https://solidity.readthedocs.io/en/v0.5.7/using-the-compiler.html#output-description
*
*
*/
compileFile(filepath, root = path.resolve(__dirname, '../contracts')) {
this.log.print(Logger.state.MASTER,
"Config: ",
" Root contract directory: " + root,
"\n"
);
this.log.print(Logger.state.NORMAL, "Generating solc_input...\n");
return this.compile(SolcUtil.generateSolcInputFile(filepath, root));
}
/**
* A function which compiles a raw "contract solc input name" and source code.
*
* @param {string} base - The "raw contract solc input name" (i.e. 'erc20/ERC20Interface.sol')
* @param {string} src - The raw Solidity smart contract code as a string.
* @return {object} output - The returned output generated by solc. See: https://solidity.readthedocs.io/en/v0.5.7/using-the-compiler.html#output-description
*/
compileSource(base, src) {
return this.compile(SolcUtil.generateSolcInput(base, src));
}
//Not working... :/ Uncertain how to handle this sort of callback stuff synchronously
async getPackageVersion(module) {
//Assume package-lock.json exists, by default
let packageJSON = "../package-lock.json";
/* if(fs.access(packageJSON, fs.constants.F_OK)){
packageJSON = "../package.json";
if(fs.access(packageJSON, fs.constants.F_OK)){
throw "Neither package.json or package-lock.json exists";
}
}*/
fs.access(packageJSON, fs.constants.F_OK, (err) => {
if(err) {
packageJSON = "../package.json";
fs.access(packageJSON, fs.constants.F_OK, (err2) => {
if(err2) {
throw "Neither package.json or package-lock.json exists\n" + err + "\n" + err2;
} else {
const packages = require(packageJSON);
return packages.dependencies[module];
}
});
} else {
const packages = require(packageJSON);
console.log(packages.dependencies)
return packages.dependencies[module];
}
});
}
}
module.exports = Compiler;