locklift
Version:
Node JS framework for working with Ever contracts. Inspired by Truffle and Hardhat. Helps you to build, test, run and maintain your smart contracts.
216 lines (215 loc) • 9.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Builder = void 0;
const child_process_1 = require("child_process");
const generators_1 = require("../../generators");
const ejs_1 = __importDefault(require("ejs"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importStar(require("path"));
const underscore_1 = __importDefault(require("underscore"));
const utils_1 = require("./utils");
const logger_1 = require("../../logger");
const preload_1 = __importDefault(require("semver/preload"));
const utils_2 = require("../../utils");
const buildCache_1 = require("../../buildCache");
class Builder {
config;
compilerVersion;
options;
nameRegex = /======= (?<contract>.*) =======/g;
docRegex = /(?<doc>^{(\s|.)*?^})/gm;
constructor(config, options, compilerVersion) {
this.config = config;
this.compilerVersion = compilerVersion;
if (preload_1.default.gte(compilerVersion, "0.72.0") && config.linkerPath) {
logger_1.logger.printWarn("Linker is no longer used as of version 72+ and can be removed from the configuration");
}
if (preload_1.default.eq(compilerVersion, "0.72.0")) {
logger_1.logger.printWarn("0.72.0 compiler is considered unsafe and is not recommended. Use at your own risk!");
}
this.options = options;
}
static create(config, options) {
const matchedCompilerVersion = (0, child_process_1.execSync)(config.compilerPath + " --version")
.toString()
.trim()
.match(/(?<=Version: |sold)(.*)(?=\+commit)/);
if (!matchedCompilerVersion) {
throw new Error("Cannot get compiler version");
}
return new Builder(config, options, matchedCompilerVersion[0]);
}
async buildContracts() {
const contractsTree = (0, utils_2.getContractsTree)(this.options.contracts);
const { contractArtifacts, contractsToBuild: externalContracts } = await (0, utils_1.resolveExternalContracts)(this.config.externalContracts);
const totalContracts = [...contractsTree.map(el => el.path), ...externalContracts];
const buildCache = new buildCache_1.BuildCache(totalContracts, this.options.force, this.options.build, this.config);
const contractsToBuild = await buildCache.buildTree();
logger_1.logger.printInfo(`Found ${totalContracts.length} sources`);
if (contractsToBuild.length > 0) {
logger_1.logger.printInfo(`Found ${contractsToBuild.length} changes, compiling...`);
try {
await this.compileContracts(contractsToBuild);
logger_1.logger.printInfo("Built");
buildCache.applyCurrentCache();
}
catch (err) {
if (err) {
logger_1.logger.printError(err);
}
return false;
}
}
else {
logger_1.logger.printInfo("No changes found, skip compilation");
}
if (contractArtifacts.length > 0) {
contractArtifacts.forEach(artifact => fs_1.default.copyFileSync(artifact, (0, path_1.resolve)(this.options.build, path_1.default.basename(artifact))));
}
if (this.config.externalContractsArtifacts) {
(0, generators_1.copyExternalArtifacts)(this.config.externalContractsArtifacts, this.options.build);
}
(0, generators_1.typeGenerator)(this.options.build, ...(this.options.externalAbiFiles || []));
logger_1.logger.printInfo("factorySource generated");
return true;
}
compileContracts(contractsToBuild) {
const contracts = contractsToBuild
.map(el => ({ path: (0, path_1.resolve)(el) }))
.map(el => ({
...el,
contractFileName: (0, utils_1.extractContractName)(el.path),
}));
if (this.config.mode === "solc") {
logger_1.logger.printInfo("Compiling with solc");
return (0, utils_1.compileBySolC)({
contracts,
compilerPath: this.config.compilerPath,
compilerParams: this.config.compilerParams,
compilerVersion: this.compilerVersion,
linkerPath: this.config.linkerPath,
disableIncludePath: this.options.disableIncludePath,
linkerLibPath: this.config.linkerLibPath,
buildFolder: this.options.build,
});
}
if (this.config.mode === "sold") {
logger_1.logger.printInfo("Compiling with sold");
return (0, utils_1.compileBySolD)({
contracts,
compilerPath: this.config.compilerPath,
compilerParams: this.config.compilerParams,
compilerVersion: this.compilerVersion,
disableIncludePath: this.options.disableIncludePath,
buildFolder: this.options.build,
soldPath: this.config.soldPath,
});
}
}
buildDocs() {
const contractsTree = (0, utils_2.getContractsTree)(this.options.contracts);
try {
logger_1.logger.printInfo(`Found ${contractsTree.length} sources`);
let docs = [];
contractsTree.map(({ path }) => {
logger_1.logger.printInfo(`Building ${path}`);
const output = (0, utils_1.execSyncWrapper)(
//@ts-ignore
`cd ${this.options.build} && ${this.config.compilerPath} ./../${path} --${this.options.mode}`);
logger_1.logger.printInfo(`Compiled ${path}`);
docs = [...docs, ...this.parseDocs(output.toString())];
});
// Filter duplicates by (path, name)
docs = docs.reduce((acc, doc) => {
if (acc.find(({ path, name }) => path === doc.path && name === doc.name)) {
return acc;
}
return [...acc, doc];
}, []);
// Sort docs by name (A-Z)
docs = docs.sort((a, b) => (a.name < b.name ? -1 : 1));
// Save docs in markdown format
const render = ejs_1.default.render(fs_1.default.readFileSync((0, path_1.resolve)(__dirname, "./../templates/index.ejs")).toString(), {
docs,
}, {
rmWhitespace: true,
});
//@ts-ignore
fs_1.default.writeFileSync(
//@ts-ignore
(0, path_1.resolve)(process.cwd(), this.options.docs, "index.md"), render);
logger_1.logger.printInfo("Docs generated successfully!");
}
catch (e) {
logger_1.logger.printError(e);
return false;
}
return true;
}
parseDocs(output) {
const contracts = [...output.matchAll(this.nameRegex)]
.map(m => m.groups.contract)
// For the target contracts compiler returns relative path
// and for dependency contracts paths are absolute
// Make them all absolute
.map(c => (0, path_1.resolve)(process.cwd(), this.options.build, c));
const docs = [...output.matchAll(this.docRegex)].map(m =>
//@ts-ignore
JSON.parse(m.groups?.doc));
return underscore_1.default.zip(contracts, docs).reduce((acc, [contract, doc]) => {
const [path, name] = contract.split(":");
// Check name matches the "include" pattern and contract is located in the "contracts" dir
if (
//@ts-ignore
name.match(new RegExp(this.options.include)) !== null &&
path.startsWith(`${process.cwd()}/${this.options.contracts}`)) {
return [
...acc,
{
path: path.replace(`${process.cwd()}/`, ""),
name,
doc,
},
];
}
return acc;
}, []);
}
}
exports.Builder = Builder;