UNPKG

@openzeppelin/cli

Version:

Command-line interface for the OpenZeppelin smart contract platform

165 lines 7.87 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const upgrades_1 = require("@openzeppelin/upgrades"); const path_1 = require("path"); const fs_1 = __importDefault(require("fs")); const util_1 = require("util"); const solidity_1 = require("../../../utils/solidity"); const is_url_1 = __importDefault(require("is-url")); const find_up_1 = __importDefault(require("find-up")); /** * Starts with roots and traverses the whole depedency tree of imports, returning an array of sources * @param rootContracts * @param workingDir What's the starting working dir for resolving relative imports in roots * @param resolver */ function gatherSources(rootContracts, workingDir) { return __awaiter(this, void 0, void 0, function* () { const result = []; const queue = []; const alreadyProcessedUrls = new Set(); for (const contract of rootContracts) { queue.push({ name: path_1.relative(workingDir, contract), url: path_1.resolve(workingDir, contract), root: workingDir, }); } while (queue.length > 0) { const fileToProcess = queue.shift(); if (alreadyProcessedUrls.has(fileToProcess.url)) continue; alreadyProcessedUrls.add(fileToProcess.url); const content = yield util_1.promisify(fs_1.default.readFile)(fileToProcess.url, 'utf-8'); const fileWithContent = Object.assign(Object.assign({}, fileToProcess), { content: content.toString() }); result.push(fileWithContent); const foundImports = tryGetImports(fileWithContent); for (const foundImport of foundImports) { const importStatement = { path: foundImport, importedFrom: fileToProcess }; const resolvedImport = yield tryResolveImportFile(importStatement); if (resolvedImport !== undefined) queue.push(resolvedImport); } } return result; }); } exports.gatherSources = gatherSources; function tryGetImports(resolvedFile) { try { return solidity_1.getImports(resolvedFile.content); } catch (_a) { // The are two reasons why the parser may crash: // - the source is not valid Solidity code // - the parser has a bug // Invalid source will be better diagnosed by the compiler, meaning we shouldn't halt execution so that it gets a // chance to inspect the source. A buggy parser will produce false negatives, but since we're not able to detect // that here, it makes more sense to fail loudly, hopefully leading to a bug report by a user. upgrades_1.Loggy.noSpin.warn(__filename, 'gatherSources', 'solidity-parser-warnings', `Error while parsing ${trimFile(resolvedFile.name)}`); return []; } } function tryResolveImportFile(importStatement) { return __awaiter(this, void 0, void 0, function* () { try { return yield resolveImportFile(importStatement); } catch (err) { // Our custom fuzzy parser may yield false positives, potentially asking for imports that don't exist. This can // happen both due to parser bugs and to invalid Solidity sources the parser accepts. Because of this, we carry on // to the compiler instead of erroring out: if an import is indeed missing or the code is incorrect, the compiler // will complain about this with a more accurate error message. upgrades_1.Loggy.noSpin.warn(__filename, 'gatherSources', 'compile-warnings', `${err.message}`); } }); } function resolveImportFile({ path, importedFrom }) { var _a; return __awaiter(this, void 0, void 0, function* () { // We support the following import paths: // 1- An absolute path: /foo/bar/baz.sol // 2- A path relative to the current file: ../foo/bar.sol // 3- A path relative to the current project root: contracts/foo/bar.sol // 4- A path to a dependency: @openzeppelin/contracts/foo/bar.sol if (is_url_1.default(path)) { throw new Error(`Error resolving '${trimFile(path)}': URL imports are not supported`); } // 1- Absolute paths if (path_1.isAbsolute(path)) { if (yield fileExists(path)) { return Object.assign(Object.assign({}, importedFrom), { name: path, url: path }); } else { throw new Error(`Could not find file '${trimFile(path)}'`); } } // 2- Relative paths if (path.startsWith('.')) { const url = path_1.resolve(path_1.dirname(importedFrom.url), path); if (yield fileExists(url)) { // If relativeTo is absolute, then url and name are the same // so the global name is absolute as well const name = path_1.isAbsolute(importedFrom.name) ? url : path_1.join(path_1.dirname(importedFrom.name), path); return Object.assign(Object.assign({}, importedFrom), { name, url }); } else { throw new Error(`Could not find file ${trimFile(path)} relative to ${importedFrom.name} in project ${importedFrom.root}`); } } // 3- Path relative to project root const localUrl = path_1.join(importedFrom.root, path); if (yield fileExists(localUrl)) { const name = path_1.join((_a = importedFrom.dependency, (_a !== null && _a !== void 0 ? _a : '')), path); return Object.assign(Object.assign({}, importedFrom), { name, url: localUrl }); } // 4- Path to dependency (only if previous one did not match) const dependencyUrl = resolveDependency(path, importedFrom); const dependencyRoot = yield find_up_1.default('package.json', { cwd: dependencyUrl }); if (dependencyRoot === undefined) throw new Error(`Could not find package root for contract ${dependencyUrl}`); const dependencyName = require(dependencyRoot).name; return { name: path, dependency: dependencyName, url: dependencyUrl, root: path_1.dirname(dependencyRoot), }; }); } function resolveDependency(path, importedFrom) { try { return require.resolve(path, { paths: [importedFrom.root] }); } catch (err) { throw new Error(`Could not find ${trimFile(path)} imported from ${importedFrom.url}`); } } function fileExists(file) { return __awaiter(this, void 0, void 0, function* () { try { return (yield util_1.promisify(fs_1.default.stat)(file)).isFile(); } catch (err) { if (err.code === 'ENOENT') return false; throw err; } }); } function trimFile(file) { return file.length < 100 ? file : file.substring(0, 100) + '...'; } //# sourceMappingURL=SourcesGatherer.js.map