UNPKG

vasku

Version:

TVM-Solidity contract development framework

272 lines (271 loc) 8.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.readMetadata = exports.readContracts = exports.compile = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const cache_1 = require("./cache"); const glob_1 = require("glob"); const make_1 = require("./make"); const artifacts_1 = require("./artifacts"); const generateIndex_1 = require("./generators/generateIndex"); async function compile(config, all = false) { const cache = all ? {} : (0, cache_1.readCache)(config); const contracts = readContracts(config.paths); const metadata = readMetadata(config.paths, cache, contracts); const newOrChangedContracts = getNewOrChangedContracts(cache, metadata); const importers = reverseImports(metadata); const candidates = getCandidates(importers, newOrChangedContracts); const includes = readIncludes(config); const excludes = readExcludes(config); const sources = readSources(includes, excludes); const contractsToCompile = getContractToCompile(config.paths, sources, candidates); if (contractsToCompile.length === 0) return; await (0, make_1.make)(config, contractsToCompile); (0, generateIndex_1.generateIndex)(config.paths, sources); (0, artifacts_1.removeOldArtifacts)(config.paths, sources); (0, cache_1.writeCache)(config, metadata); } exports.compile = compile; /** * Read all files from contracts directory * @param config * @return * ['A.tsol', 'B.tsol', 'x/C.tsol'] */ function readContracts(config) { const directory = path_1.default.resolve(process.cwd(), config.contracts); return (0, glob_1.globSync)(path_1.default.resolve(directory, '**'), { nodir: true }) .map(value => path_1.default.relative(directory, value)); } exports.readContracts = readContracts; /** * If the contract has not changed, read the metadata from the cache. If changed, read from file * @param config * @param cache * { * 'A.tsol': { * modificationTime: 1666666666, * imports: ['IA.tsol'] * } * } * @param contracts * ['A.tsol', 'B.tsol'] * @return * { * 'A.tsol': { * modificationTime: 1666666666, * imports: ['IA.tsol'] * }, * 'B.tsol': { * modificationTime: 1666666666, * imports: ['IB.tsol'] * } * } */ function readMetadata(config, cache, contracts) { return contracts.reduce((meta, contract) => { const file = path_1.default.resolve(process.cwd(), config.contracts, contract); const modificationTime = fs_extra_1.default.statSync(file).mtime.getTime(); const cacheContract = cache[contract]; meta[contract] = (cacheContract !== undefined && cacheContract.modificationTime === modificationTime) ? cacheContract : { modificationTime, imports: readImports(config, file) }; return meta; }, {}); } exports.readMetadata = readMetadata; /** * Read imports from contract file * @param config * @param file * 'A.tsol' * @return * ['B.tsol', 'IA.tsol'] */ function readImports(config, file) { const result = []; const relativeDirectory = path_1.default.relative(path_1.default.resolve(process.cwd(), config.contracts), path_1.default.dirname(file)); const content = fs_extra_1.default.readFileSync(file, { encoding: 'utf8' }); const regexp = /(?<=import ").+(?=";)|(?<=import ').+(?=';)/g; const maths = content.matchAll(regexp); for (const [importRelative] of maths) result.push(path_1.default.relative(relativeDirectory, importRelative)); return result; } /** * Returns contracts that are not in the cache, or are in the cache and the modification time has been changed * @param cache * { * 'A.tsol': { * modificationTime: 1666666666, * imports: ['IA.tsol'] * }, * 'B.tsol': { * modificationTime: 1666666666, * imports: ['IA.tsol'] * } * } * @param metadata * { * 'A.tsol': { * modificationTime: 1666666666, * imports: ['IA.tsol'] * }, * 'B.tsol': { * modificationTime: 1666666666, * imports: ['IB.tsol'] * } * } * @return * ['B.tsol'] */ function getNewOrChangedContracts(cache, metadata) { const result = []; for (const contract in metadata) { const cacheContract = cache[contract]; const metaContract = metadata[contract]; if (cacheContract === undefined || cacheContract.modificationTime !== metaContract.modificationTime) result.push(contract); } return result; } /** * Return map (contract) => (contracts that depend on it) * @param metadata * { * 'A.tsol': { * modificationTime: 1666666666, * imports: ['B.tsol', 'C.tsol'] * }, * 'B.tsol': { * modificationTime: 1666666666, * imports: [] * }, * 'C.tsol': { * modificationTime: 1666666666, * imports: ['D.tsol'] * }, * 'D.tsol': { * modificationTime: 1666666666, * imports: [] * } * } * @return * { * 'B.tsol': ['A.tsol'], * 'C.tsol': ['A.tsol'], * 'D.tsol': ['C.tsol'] * } */ function reverseImports(metadata) { const result = {}; for (const contract in metadata) { metadata[contract].imports.forEach(value => { if (result[value] === undefined) result[value] = [contract]; else result[value].push(contract); }); } return result; } /** * Return a set of candidate contracts to compilation * @param importers * { * 'B.tsol': ['A.tsol'], * 'C.tsol': ['A.tsol'], * 'D.tsol': ['C.tsol'] * } * @param contracts * ['B.tsol'] * @return * Set {'A.tsol', 'B.tsol'} */ function getCandidates(importers, contracts) { const set = new Set(); let list = [...contracts]; for (let i = 0; i < list.length; ++i) { const contract = list[i]; if (set.has(contract)) continue; set.add(contract); const contractImporters = importers[contract]; if (contractImporters !== undefined) list = list.concat(contractImporters); } return set; } /** * Return a set of contracts that included to compilation * @param config * @return * Set {'A.tsol', 'B.tsol'} */ function readIncludes(config) { return readFiles(config.paths, config.compile.include); } /** * Return a set of contracts that excluded from compilation * @param config * @return * Set {'A.tsol', 'B.tsol'} */ function readExcludes(config) { return readFiles(config.paths, config.compile.exclude); } /** * Return a set of contracts using glob * @param config * @param globs * ['*.tsol', '*.sol'] * @return * Set {'A.tsol', 'B.tsol'} */ function readFiles(config, globs) { const directory = path_1.default.resolve(process.cwd(), config.contracts); return globs.reduce((set, value) => (0, glob_1.globSync)(path_1.default.resolve(directory, value), { nodir: true }) .map(value => path_1.default.relative(directory, value)) .reduce((set, value) => set.add(value), set), new Set()); } /** * Exclude contracts from include * @param includes * Set {'A.tsol', 'B.tsol'} * @param excludes * Set {'A.tsol'} * @return * Set {'B.tsol'} */ function readSources(includes, excludes) { return [...includes].reduce((set, value) => excludes.has(value) ? set : set.add(value), new Set()); } /** * Return contract array to compilation * @param config * @param sources * Set(3) {'Counter.tsol', 'd/B.tsol', 'd/A.tsol'} * @param candidates * Set(4) { * 'Counter.tsol', * 'interface/ICounter.tsol', * 'd/B.tsol', * 'd/A.tsol' * } * @return * ['Counter.tsol', 'd/B.tsol', 'd/A.tsol'] */ function getContractToCompile(config, sources, candidates) { return [...sources].reduce((result, value) => { if (candidates.has(value) || !(0, artifacts_1.buildsArtifactsExists)(config, value)) result.push(value); return result; }, []); }