From d52234e2d62e8bbeccde6d1cb3ab1b7401e3f8b0 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 5 Aug 2021 16:24:22 -0400 Subject: [PATCH 01/35] Add supplier/* branches to GHA --- .github/workflows/nodejs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 1dca82151c9..d249bafb280 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -7,6 +7,7 @@ on: - master - develop - next + - supplier/* pull_request: branches: From ae27ad688d562fac216e82ee11cdc915e15a08e9 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Tue, 27 Jul 2021 19:02:58 -0400 Subject: [PATCH 02/35] Move normalizeSolcVersion out of VersionRange --- .../compilerSupplier/loadingStrategies/Docker.js | 4 ++-- .../compilerSupplier/loadingStrategies/Native.js | 4 ++-- .../compilerSupplier/loadingStrategies/VersionRange.js | 5 ----- .../compilerSupplier/normalizeSolcVersion.js | 6 ++++++ 4 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index 6ba98c92f68..929ebd7d474 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -4,7 +4,7 @@ const { execSync } = require("child_process"); const ora = require("ora"); const semver = require("semver"); const LoadingStrategy = require("./LoadingStrategy"); -const VersionRange = require("./VersionRange"); +const { normalizeSolcVersion } = require("../normalizeSolcVersion"); class Docker extends LoadingStrategy { async load() { @@ -95,7 +95,7 @@ class Docker extends LoadingStrategy { const version = execSync( "docker run ethereum/solc:" + image + " --version" ); - const normalized = new VersionRange(this.config).normalizeSolcVersion( + const normalized = normalizeSolcVersion( version ); this.addFileToCache(normalized, fileName); diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js index 25c95758b11..f9137124193 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js @@ -1,6 +1,6 @@ const { execSync } = require("child_process"); +const { normalizeSolcVersion } = require("../normalizeSolcVersion"); const LoadingStrategy = require("./LoadingStrategy"); -const VersionRange = require("./VersionRange"); class Native extends LoadingStrategy { load() { @@ -29,7 +29,7 @@ class Native extends LoadingStrategy { } catch (error) { throw this.errors("noNative", null, error); } - return new VersionRange(this.config).normalizeSolcVersion(version); + return normalizeSolcVersion(version); } } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index c9918356b8c..f3a9d7abac8 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -208,11 +208,6 @@ class VersionRange extends LoadingStrategy { } } - normalizeSolcVersion(input) { - const version = String(input); - return version.split(":")[1].trim(); - } - versionIsCached(version) { const cachedCompilerFileNames = fs.readdirSync(this.compilerCachePath); const cachedVersions = cachedCompilerFileNames.map(fileName => { diff --git a/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js b/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js new file mode 100644 index 00000000000..68c23605747 --- /dev/null +++ b/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js @@ -0,0 +1,6 @@ +const normalizeSolcVersion = (input) => { + const version = String(input); + return version.split(":")[1].trim(); +}; + +module.exports = { normalizeSolcVersion }; From e8bd8334a66b26616ac64d11499378d9913f6f24 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 28 Jul 2021 16:29:38 -0400 Subject: [PATCH 03/35] Separate LoadingStrategy cache into own class - Create new method cache.getCachedFileNames() in order to remove coupling between VersionRange and compilerCachePath --- .../compilerSupplier/Cache.js | 37 +++++++++++++++++++ .../loadingStrategies/Docker.js | 13 +++++-- .../loadingStrategies/LoadingStrategy.js | 29 +-------------- .../loadingStrategies/VersionRange.js | 20 ++++++---- .../test/compilerSupplier/index.js | 9 +++-- .../loadingStrategies/VersionRange.js | 36 +++++++++--------- 6 files changed, 83 insertions(+), 61 deletions(-) create mode 100644 packages/compile-solidity/compilerSupplier/Cache.js diff --git a/packages/compile-solidity/compilerSupplier/Cache.js b/packages/compile-solidity/compilerSupplier/Cache.js new file mode 100644 index 00000000000..97be6887dd1 --- /dev/null +++ b/packages/compile-solidity/compilerSupplier/Cache.js @@ -0,0 +1,37 @@ +const Config = require("@truffle/config"); +const path = require("path"); +const fs = require("fs"); + +class Cache { + constructor() { + const compilersDir = path.resolve( + Config.getTruffleDataDirectory(), + "compilers" + ); + const compilerCachePath = path.resolve(compilersDir, "node_modules"); // because babel binds to require & does weird things + if (!fs.existsSync(compilersDir)) fs.mkdirSync(compilersDir); + if (!fs.existsSync(compilerCachePath)) fs.mkdirSync(compilerCachePath); // for 5.0.8 users + + this.compilerCachePath = compilerCachePath; + } + + getCachedFileNames() { + return fs.readdirSync(this.compilerCachePath); + } + + addFileToCache(code, fileName) { + const filePath = this.resolveCache(fileName); + fs.writeFileSync(filePath, code); + } + + fileIsCached(fileName) { + const file = this.resolveCache(fileName); + return fs.existsSync(file); + } + + resolveCache(fileName) { + return path.resolve(this.compilerCachePath, fileName); + } +}; + +module.exports = Cache; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index 929ebd7d474..19dae4c0fd9 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -4,9 +4,16 @@ const { execSync } = require("child_process"); const ora = require("ora"); const semver = require("semver"); const LoadingStrategy = require("./LoadingStrategy"); +const Cache = require("../Cache"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); class Docker extends LoadingStrategy { + constructor(...args) { + super(...args); + + this.cache = new Cache(); + } + async load() { // Set a sensible limit for maxBuffer // See https://github.com/nodejs/node/pull/23027 @@ -68,8 +75,8 @@ class Docker extends LoadingStrategy { const fileName = image + ".version"; // Skip validation if they've validated for this image before. - if (this.fileIsCached(fileName)) { - const cachePath = this.resolveCache(fileName); + if (this.cache.fileIsCached(fileName)) { + const cachePath = this.cache.resolveCache(fileName); return fs.readFileSync(cachePath, "utf-8"); } // Image specified @@ -98,7 +105,7 @@ class Docker extends LoadingStrategy { const normalized = normalizeSolcVersion( version ); - this.addFileToCache(normalized, fileName); + this.cache.addFileToCache(normalized, fileName); return normalized; } } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js index 62c14343947..2289592aaa5 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js @@ -1,7 +1,3 @@ -const Config = require("@truffle/config"); -const path = require("path"); -const fs = require("fs"); - class LoadingStrategy { constructor(options) { const defaultConfig = { @@ -14,20 +10,6 @@ class LoadingStrategy { "https://registry.hub.docker.com/v2/repositories/ethereum/solc/tags/" }; this.config = Object.assign({}, defaultConfig, options); - const compilersDir = path.resolve( - Config.getTruffleDataDirectory(), - "compilers" - ); - const compilerCachePath = path.resolve(compilersDir, "node_modules"); // because babel binds to require & does weird things - if (!fs.existsSync(compilersDir)) fs.mkdirSync(compilersDir); - if (!fs.existsSync(compilerCachePath)) fs.mkdirSync(compilerCachePath); // for 5.0.8 users - - this.compilerCachePath = compilerCachePath; - } - - addFileToCache(code, fileName) { - const filePath = this.resolveCache(fileName); - fs.writeFileSync(filePath, code); } errors(kind, input, error) { @@ -66,11 +48,6 @@ class LoadingStrategy { return new Error(kinds[kind]); } - fileIsCached(fileName) { - const file = this.resolveCache(fileName); - return fs.existsSync(file); - } - load(_userSpecification) { throw new Error( "Abstract method LoadingStrategy.load is not implemented for this strategy." @@ -98,10 +75,6 @@ class LoadingStrategy { } } } - - resolveCache(fileName) { - return path.resolve(this.compilerCachePath, fileName); - } -} +}; module.exports = LoadingStrategy; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index f3a9d7abac8..5fb8de338cf 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -1,13 +1,19 @@ const debug = require("debug")("compile:compilerSupplier"); const requireFromString = require("require-from-string"); -const fs = require("fs"); const originalRequire = require("original-require"); const axios = require("axios").default; const semver = require("semver"); const solcWrap = require("solc/wrapper"); const LoadingStrategy = require("./LoadingStrategy"); +const Cache = require("../Cache"); class VersionRange extends LoadingStrategy { + constructor(...args) { + super(...args); + + this.cache = new Cache(); + } + compilerFromString(code) { const markedListeners = this.markListeners(); try { @@ -37,7 +43,7 @@ class VersionRange extends LoadingStrategy { getCachedSolcByFileName(fileName) { const markedListeners = this.markListeners(); try { - const filePath = this.resolveCache(fileName); + const filePath = this.cache.resolveCache(fileName); const soljson = originalRequire(filePath); debug("soljson %o", soljson); return solcWrap(soljson); @@ -48,7 +54,7 @@ class VersionRange extends LoadingStrategy { // Range can also be a single version specification like "0.5.0" getCachedSolcByVersionRange(version) { - const cachedCompilerFileNames = fs.readdirSync(this.compilerCachePath); + const cachedCompilerFileNames = this.cache.getCachedFileNames(); const validVersions = cachedCompilerFileNames.filter(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return semver.satisfies(match[0], version); @@ -62,7 +68,7 @@ class VersionRange extends LoadingStrategy { } getCachedSolcFileName(commit) { - const cachedCompilerFileNames = fs.readdirSync(this.compilerCachePath); + const cachedCompilerFileNames = this.cache.getCachedFileNames(); return cachedCompilerFileNames.find(fileName => { return fileName.includes(commit); }); @@ -110,7 +116,7 @@ class VersionRange extends LoadingStrategy { try { const response = await axios.get(url, { maxRedirects: 50 }); events.emit("downloadCompiler:succeed"); - this.addFileToCache(response.data, fileName); + this.cache.addFileToCache(response.data, fileName); return this.compilerFromString(response.data); } catch (error) { events.emit("downloadCompiler:fail"); @@ -137,7 +143,7 @@ class VersionRange extends LoadingStrategy { if (!fileName) throw this.errors("noVersion", versionToUse); - if (this.fileIsCached(fileName)) + if (this.cache.fileIsCached(fileName)) return this.getCachedSolcByFileName(fileName); return this.getSolcByUrlAndCache(fileName); } @@ -209,7 +215,7 @@ class VersionRange extends LoadingStrategy { } versionIsCached(version) { - const cachedCompilerFileNames = fs.readdirSync(this.compilerCachePath); + const cachedCompilerFileNames = this.cache.getCachedFileNames(); const cachedVersions = cachedCompilerFileNames.map(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return match[0]; diff --git a/packages/compile-solidity/test/compilerSupplier/index.js b/packages/compile-solidity/test/compilerSupplier/index.js index 7fbd1252165..00220486ab1 100644 --- a/packages/compile-solidity/test/compilerSupplier/index.js +++ b/packages/compile-solidity/test/compilerSupplier/index.js @@ -2,6 +2,7 @@ const assert = require("assert"); const sinon = require("sinon"); const axios = require("axios"); const CompilerSupplier = require("../../compilerSupplier"); +const Cache = require("../../compilerSupplier/Cache"); const { Docker, Native, @@ -136,11 +137,11 @@ describe("CompilerSupplier", () => { describe("when a user specifies the compiler url root", () => { beforeEach(() => { + sinon.stub(Cache.prototype, "addFileToCache"); + sinon.stub(Cache.prototype, "fileIsCached").returns(false); sinon.stub(VersionRange.prototype, "getSolcVersions") .returns(allVersions); - sinon.stub(VersionRange.prototype, "addFileToCache"); sinon.stub(VersionRange.prototype, "versionIsCached").returns(false); - sinon.stub(VersionRange.prototype, "fileIsCached").returns(false); sinon.stub(VersionRange.prototype, "compilerFromString"); sinon.stub(axios, "get") .withArgs( @@ -149,10 +150,10 @@ describe("CompilerSupplier", () => { .returns({ data: "response" }); }); afterEach(() => { + Cache.prototype.addFileToCache.restore(); + Cache.prototype.fileIsCached.restore(); VersionRange.prototype.getSolcVersions.restore(); - VersionRange.prototype.addFileToCache.restore(); VersionRange.prototype.versionIsCached.restore(); - VersionRange.prototype.fileIsCached.restore(); VersionRange.prototype.compilerFromString.restore(); axios.get.restore(); }); diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js index 03dfa21f9e3..7cab88a7f38 100644 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js @@ -97,11 +97,11 @@ describe("VersionRange loading strategy", () => { describe("when a version constraint is specified", () => { beforeEach(() => { sinon.stub(instance, "getSolcByUrlAndCache"); - sinon.stub(instance, "fileIsCached").returns(false); + sinon.stub(instance.cache, "fileIsCached").returns(false); }); afterEach(() => { instance.getSolcByUrlAndCache.restore(); - instance.fileIsCached.restore(); + instance.cache.fileIsCached.restore(); }); it("calls findNewstValidVersion to determine which version to fetch", async () => { @@ -117,10 +117,10 @@ describe("VersionRange loading strategy", () => { describe("when the version is cached", () => { beforeEach(() => { - sinon.stub(instance, "fileIsCached").returns(true); + sinon.stub(instance.cache, "fileIsCached").returns(true); }); afterEach(() => { - instance.fileIsCached.restore(); + instance.cache.fileIsCached.restore(); }); it("calls getCachedSolcByFileName", async () => { @@ -135,19 +135,19 @@ describe("VersionRange loading strategy", () => { describe("when the version is not cached", () => { beforeEach(() => { - sinon.stub(instance, "fileIsCached").returns(false); + sinon.stub(instance.cache, "fileIsCached").returns(false); + sinon.stub(instance.cache, "addFileToCache"); sinon.stub(instance, "compilerFromString").returns("compiler"); - sinon.stub(instance, "addFileToCache"); }); afterEach(() => { - instance.fileIsCached.restore(); + instance.cache.fileIsCached.restore(); + instance.cache.addFileToCache.restore(); instance.compilerFromString.restore(); - instance.addFileToCache.restore(); }); it("eventually calls addFileToCache and compilerFromString", async () => { await instance.getSolcFromCacheOrUrl("0.5.1"); - assert(instance.addFileToCache.called); + assert(instance.cache.addFileToCache.called); assert(instance.compilerFromString.called); }).timeout(60000); }); @@ -174,6 +174,7 @@ describe("VersionRange loading strategy", () => { }); describe("when the file is not cached", () => { + let commitString; beforeEach(() => { // commit string for v 0.5.1 commitString = "commit.c8a2cb62"; @@ -183,16 +184,13 @@ describe("VersionRange loading strategy", () => { .returns(undefined); sinon.stub(axios, "get") .returns(Promise.resolve({ data: "the stuff" })); - sinon.stub(instance, "addFileToCache"); + sinon.stub(instance.cache, "addFileToCache"); sinon.stub(instance, "compilerFromString"); - expectedUrl = - instance.config.compilerRoots[0] + - "soljson-v0.5.1+commit.c8a2cb62.js"; }); afterEach(() => { instance.getCachedSolcFileName.restore(); axios.get.restore(); - instance.addFileToCache.restore(); + instance.cache.addFileToCache.restore(); instance.compilerFromString.restore(); }); @@ -218,7 +216,7 @@ describe("VersionRange loading strategy", () => { .stub(axios, "get") .withArgs(`${instance.config.compilerRoots[0]}${fileName}`) .returns({ data: "requestReturn" }); - sinon.stub(instance, "addFileToCache").withArgs("requestReturn"); + sinon.stub(instance.cache, "addFileToCache").withArgs("requestReturn"); sinon .stub(instance, "compilerFromString") .withArgs("requestReturn") @@ -226,14 +224,14 @@ describe("VersionRange loading strategy", () => { }); afterEach(() => { axios.get.restore(); - instance.addFileToCache.restore(); + instance.cache.addFileToCache.restore(); instance.compilerFromString.restore(); }); it("calls addFileToCache with the response and the file name", async () => { - result = await instance.getSolcByUrlAndCache(fileName, 0); + const result = await instance.getSolcByUrlAndCache(fileName, 0); assert( - instance.addFileToCache.calledWith("requestReturn", "someSolcFile") + instance.cache.addFileToCache.calledWith("requestReturn", "someSolcFile") ); assert(result === "success"); }); @@ -241,7 +239,7 @@ describe("VersionRange loading strategy", () => { describe(".findNewestValidVersion(version, allVersions)", () => { it("returns the version name of the newest valid version", () => { - expectedResult = "0.5.4"; + const expectedResult = "0.5.4"; assert( instance.findNewestValidVersion("^0.5.0", allVersions) === expectedResult From 7c4afa5d3d75e613afd943737b73967b3dda8607 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 28 Jul 2021 17:26:40 -0400 Subject: [PATCH 04/35] Extract listener cleanup into function --- .../loadingStrategies/LoadingStrategy.js | 22 -------------- .../loadingStrategies/Local.js | 7 +++-- .../loadingStrategies/VersionRange.js | 9 +++--- .../compilerSupplier/observeListeners.js | 30 +++++++++++++++++++ 4 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 packages/compile-solidity/compilerSupplier/observeListeners.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js index 2289592aaa5..fe6ffaf3055 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js @@ -53,28 +53,6 @@ class LoadingStrategy { "Abstract method LoadingStrategy.load is not implemented for this strategy." ); } - - markListeners() { - return { - uncaughtException: new Set(process.listeners("uncaughtException")), - unhandledRejection: new Set(process.listeners("unhandledRejection")), - }; - } - - /** - * Cleans up error listeners left by soljson - * Use with `markListeners()` - */ - removeListener(markedListeners) { - for (const eventName in markedListeners) { - const marked = markedListeners[eventName]; - for (const listener of process.listeners(eventName)) { - if (!marked.has(listener)) { - process.removeListener(eventName, listener); - } - } - } - } }; module.exports = LoadingStrategy; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js index 8a42a07ee13..0c6b1d6dbc4 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js @@ -2,6 +2,7 @@ const path = require("path"); const originalRequire = require("original-require"); const LoadingStrategy = require("./LoadingStrategy"); const solcWrap = require("solc/wrapper"); +const observeListeners = require("../observeListeners"); class Local extends LoadingStrategy { load(localPath) { @@ -9,9 +10,9 @@ class Local extends LoadingStrategy { } getLocalCompiler(localPath) { - const markedListeners = this.markListeners(); + const listeners = observeListeners(); try { - let soljson, compilerPath, wrapped; + let soljson, compilerPath; compilerPath = path.isAbsolute(localPath) ? localPath : path.resolve(process.cwd(), localPath); @@ -24,7 +25,7 @@ class Local extends LoadingStrategy { //HACK: if it has a compile function, assume it's already wrapped return soljson.compile ? soljson : solcWrap(soljson); } finally { - this.removeListener(markedListeners); + listeners.cleanup(); } } } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 5fb8de338cf..9f55adc5b26 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -6,6 +6,7 @@ const semver = require("semver"); const solcWrap = require("solc/wrapper"); const LoadingStrategy = require("./LoadingStrategy"); const Cache = require("../Cache"); +const observeListeners = require("../observeListeners"); class VersionRange extends LoadingStrategy { constructor(...args) { @@ -15,12 +16,12 @@ class VersionRange extends LoadingStrategy { } compilerFromString(code) { - const markedListeners = this.markListeners(); + const listeners = observeListeners(); try { const soljson = requireFromString(code); return solcWrap(soljson); } finally { - this.removeListener(markedListeners); + listeners.cleanup(); } } @@ -41,14 +42,14 @@ class VersionRange extends LoadingStrategy { } getCachedSolcByFileName(fileName) { - const markedListeners = this.markListeners(); + const listeners = observeListeners(); try { const filePath = this.cache.resolveCache(fileName); const soljson = originalRequire(filePath); debug("soljson %o", soljson); return solcWrap(soljson); } finally { - this.removeListener(markedListeners); + listeners.cleanup(); } } diff --git a/packages/compile-solidity/compilerSupplier/observeListeners.js b/packages/compile-solidity/compilerSupplier/observeListeners.js new file mode 100644 index 00000000000..d4880d05e0f --- /dev/null +++ b/packages/compile-solidity/compilerSupplier/observeListeners.js @@ -0,0 +1,30 @@ +const observeListeners = () => { + const listeners = new ObservedListeners(); + + return listeners; +}; + +class ObservedListeners { + constructor() { + this.listeners = { + uncaughtException: new Set(process.listeners("uncaughtException")), + unhandledRejection: new Set(process.listeners("unhandledRejection")), + }; + } + + /** + * Cleans up error listeners left by soljson + */ + cleanup() { + for (const eventName in this.listeners) { + const marked = this.listeners[eventName]; + for (const listener of process.listeners(eventName)) { + if (!marked.has(listener)) { + process.removeListener(eventName, listener); + } + } + } + } +} + +module.exports = observeListeners; From 322a3c47256982faa6744ec909f872e8bedb6813 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 28 Jul 2021 18:18:31 -0400 Subject: [PATCH 05/35] Remove supplier.downloadAndCacheSolc --- .../compilerSupplier/index.js | 14 -------- packages/core/lib/commands/obtain.js | 32 ++++++++++++------- packages/core/test/lib/commands/obtain.js | 10 +++--- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/index.js b/packages/compile-solidity/compilerSupplier/index.js index fd9d9f018b5..8c333534940 100644 --- a/packages/compile-solidity/compilerSupplier/index.js +++ b/packages/compile-solidity/compilerSupplier/index.js @@ -33,20 +33,6 @@ class CompilerSupplier { return new Error(message); } - async downloadAndCacheSolc(version) { - if (semver.validRange(version)) { - return await new VersionRange(this.strategyOptions).getSolcFromCacheOrUrl( - version - ); - } - - const message = - `You must specify a valid solc version to download` + - `Please ensure that the version you entered, ` + - `${version}, is valid.`; - throw new Error(message); - } - async load() { const userSpecification = this.version; diff --git a/packages/core/lib/commands/obtain.js b/packages/core/lib/commands/obtain.js index 19c0ae5d17d..d7383549e4f 100644 --- a/packages/core/lib/commands/obtain.js +++ b/packages/core/lib/commands/obtain.js @@ -15,18 +15,11 @@ module.exports = { const SUPPORTED_COMPILERS = ["--solc"]; const Config = require("@truffle/config"); const config = Config.default().with(options); - const CompilerSupplier = require("@truffle/compile-solidity") - .CompilerSupplier; - const supplierOptions = { - events: config.events, - solcConfig: config.compilers.solc - }; - const supplier = new CompilerSupplier(supplierOptions); config.events.emit("obtain:start"); if (options.solc) { - return await this.downloadAndCacheSolc({config, options, supplier}); + return await this.downloadAndCacheSolc({config, options}); } const message = @@ -38,11 +31,28 @@ module.exports = { throw new Error(message); }, - downloadAndCacheSolc: async ({config, options, supplier}) => { - const {events} = config; + downloadAndCacheSolc: async ({config, options}) => { + const { CompilerSupplier } = require("@truffle/compile-solidity"); + const semver = require("semver"); + const { events } = config; + const version = options.solc; + if (!version || !semver.validRange(version)) { + const message = + `You must specify a valid solc version to download` + + `You specified: "${version}".`; + throw new Error(message); + } + try { - const solc = await supplier.downloadAndCacheSolc(version); + const supplier = new CompilerSupplier({ + events, + solcConfig: { + ...config.compilers.solc, + version + } + }); + const { solc } = await supplier.load(); events.emit("obtain:succeed", { compiler: { version: solc.version(), diff --git a/packages/core/test/lib/commands/obtain.js b/packages/core/test/lib/commands/obtain.js index e0e268d8a9f..5b1246b2d54 100644 --- a/packages/core/test/lib/commands/obtain.js +++ b/packages/core/test/lib/commands/obtain.js @@ -10,17 +10,17 @@ describe("obtain", () => { options = {solc: "0.5.3"}; solc = {version: () => "0.5.3"}; sinon - .stub(CompilerSupplier.prototype, "downloadAndCacheSolc") - .returns(solc); + .stub(CompilerSupplier.prototype, "load") + .returns({ solc }); }); afterEach(() => { - CompilerSupplier.prototype.downloadAndCacheSolc.restore(); + CompilerSupplier.prototype.load.restore(); }); - it("calls downloadAndCacheSolc on the supplier with the version", async function () { + it("calls supplier.load()", async function () { await command.run(options); assert( - CompilerSupplier.prototype.downloadAndCacheSolc.calledWith("0.5.3") + CompilerSupplier.prototype.load.calledWith() ); }); From 50a36f30ea33c690bf2d8defb1118022c94a5b2c Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 29 Jul 2021 18:37:40 -0400 Subject: [PATCH 06/35] Get rid of LoadingStrategy.prototype.errors() As a potential stopgap: 1. Blindly replace all `noVersion` / `noWhatever` strings with analogous `NoVersionError` classes, putting them in new errors bucket module 2. For each such new error class, if the class is used by only one loading strategy, move the class to that module. --- .../compilerSupplier/errors.js | 24 +++++++++++++ .../loadingStrategies/Docker.js | 32 ++++++++++++++--- .../loadingStrategies/LoadingStrategy.js | 36 ------------------- .../loadingStrategies/Local.js | 9 ++++- .../loadingStrategies/Native.js | 11 ++++-- .../loadingStrategies/VersionRange.js | 20 +++++++---- 6 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 packages/compile-solidity/compilerSupplier/errors.js diff --git a/packages/compile-solidity/compilerSupplier/errors.js b/packages/compile-solidity/compilerSupplier/errors.js new file mode 100644 index 00000000000..b0ae00ecda3 --- /dev/null +++ b/packages/compile-solidity/compilerSupplier/errors.js @@ -0,0 +1,24 @@ +class NoVersionError extends Error { + constructor(input) { + const message = `Could not find a compiler version matching ${input}. ` + + `Please ensure you are specifying a valid version, constraint or ` + + `build in the truffle config. Run \`truffle compile --list\` to ` + + `see available versions.`; + super(message); + } +} + +class NoRequestError extends Error { + constructor(input, error) { + const message = + `Failed to complete request to: ${input}. Are you connected to ` + + `the internet?\n\n` + + error; + super(message); + } +} + +module.exports = { + NoVersionError, + NoRequestError +}; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index 19dae4c0fd9..79ab6f9ac2e 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -6,6 +6,7 @@ const semver = require("semver"); const LoadingStrategy = require("./LoadingStrategy"); const Cache = require("../Cache"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); +const { NoVersionError, NoRequestError } = require("../errors"); class Docker extends LoadingStrategy { constructor(...args) { @@ -36,7 +37,7 @@ class Docker extends LoadingStrategy { }; } catch (error) { if (error.message === "No matching version found") { - throw this.errors("noVersion", versionString); + throw new NoVersionError(versionString); } throw new Error(error); } @@ -46,7 +47,7 @@ class Docker extends LoadingStrategy { return axios.get(this.config.dockerTagsUrl, { maxRedirects: 50 }) .then(response => response.data.results.map(item => item.name)) .catch(error => { - throw this.errors("noRequest", this.config.dockerTagsUrl, error); + throw new NoRequestError(this.config.dockerTagsUrl, error); }); } @@ -80,13 +81,13 @@ class Docker extends LoadingStrategy { return fs.readFileSync(cachePath, "utf-8"); } // Image specified - if (!image) throw this.errors("noString", image); + if (!image) throw new NoStringError(image); // Docker exists locally try { execSync("docker -v"); } catch (error) { - throw this.errors("noDocker"); + throw new NoDockerError(); } // Image exists locally @@ -110,4 +111,27 @@ class Docker extends LoadingStrategy { } } +class NoDockerError extends Error { + constructor() { + super( + "You are trying to run dockerized solc, but docker is not installed." + ); + } +} + +class NoStringError extends Error { + constructor(input) { + const message = + "`compilers.solc.version` option must be a string specifying:\n" + + " - a path to a locally installed solcjs\n" + + " - a solc version or range (ex: '0.4.22' or '^0.5.0')\n" + + " - a docker image name (ex: 'stable')\n" + + " - 'native' to use natively installed solc\n" + + "Received: " + + input + + " instead."; + super(message); + } +} + module.exports = Docker; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js index fe6ffaf3055..7d20c770d07 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js @@ -12,42 +12,6 @@ class LoadingStrategy { this.config = Object.assign({}, defaultConfig, options); } - errors(kind, input, error) { - const info = "Run `truffle compile --list` to see available versions."; - - const kinds = { - noPath: "Could not find compiler at: " + input, - noVersion: - `Could not find a compiler version matching ${input}. ` + - `Please ensure you are specifying a valid version, constraint or ` + - `build in the truffle config. ${info}`, - noRequest: - "Failed to complete request to: " + - input + - ". Are you connected to the internet?\n\n" + - error, - noUrl: "compiler root URL missing", - noDocker: - "You are trying to run dockerized solc, but docker is not installed.", - noImage: - "Please pull " + - input + - " from docker before trying to compile with it.", - noNative: "Could not execute local solc binary: " + error, - noString: - "`compilers.solc.version` option must be a string specifying:\n" + - " - a path to a locally installed solcjs\n" + - " - a solc version or range (ex: '0.4.22' or '^0.5.0')\n" + - " - a docker image name (ex: 'stable')\n" + - " - 'native' to use natively installed solc\n" + - "Received: " + - input + - " instead." - }; - - return new Error(kinds[kind]); - } - load(_userSpecification) { throw new Error( "Abstract method LoadingStrategy.load is not implemented for this strategy." diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js index 0c6b1d6dbc4..b7045646842 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js @@ -20,7 +20,7 @@ class Local extends LoadingStrategy { try { soljson = originalRequire(compilerPath); } catch (error) { - throw this.errors("noPath", localPath, error); + throw new NoPathError(localPath, error); } //HACK: if it has a compile function, assume it's already wrapped return soljson.compile ? soljson : solcWrap(soljson); @@ -30,4 +30,11 @@ class Local extends LoadingStrategy { } } +class NoPathError extends Error { + constructor(input, error) { + const message = `Could not find compiler at: ${input}\n\n` + error; + super(message); + } +} + module.exports = Local; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js index f9137124193..2518c84323f 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js @@ -1,6 +1,7 @@ const { execSync } = require("child_process"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); const LoadingStrategy = require("./LoadingStrategy"); +const { NoVersionError } = require("../errors"); class Native extends LoadingStrategy { load() { @@ -16,7 +17,7 @@ class Native extends LoadingStrategy { }; } catch (error) { if (error.message === "No matching version found") { - throw this.errors("noVersion", versionString); + throw new NoVersionError(versionString); } throw new Error(error); } @@ -27,10 +28,16 @@ class Native extends LoadingStrategy { try { version = execSync("solc --version"); } catch (error) { - throw this.errors("noNative", null, error); + throw new NoNativeError(error); } return normalizeSolcVersion(version); } } +class NoNativeError extends Error { + constructor(error) { + super("Could not execute local solc binary: " + error); + } +} + module.exports = Native; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 9f55adc5b26..5e7aa2bc26c 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -7,6 +7,7 @@ const solcWrap = require("solc/wrapper"); const LoadingStrategy = require("./LoadingStrategy"); const Cache = require("../Cache"); const observeListeners = require("../observeListeners"); +const { NoVersionError, NoRequestError } = require("../errors"); class VersionRange extends LoadingStrategy { constructor(...args) { @@ -91,7 +92,7 @@ class VersionRange extends LoadingStrategy { if (this.versionIsCached(versionRange)) { return this.getCachedSolcByVersionRange(versionRange); } - throw this.errors("noVersion", versionRange); + throw new NoVersionError(versionRange); } async getSolcByCommit(commit) { @@ -122,7 +123,7 @@ class VersionRange extends LoadingStrategy { } catch (error) { events.emit("downloadCompiler:fail"); if (index >= this.config.compilerRoots.length - 1) { - throw this.errors("noRequest", "compiler URLs", error); + throw new NoRequestError("compiler URLs", error); } return this.getSolcByUrlAndCache(fileName, index + 1); } @@ -133,7 +134,7 @@ class VersionRange extends LoadingStrategy { try { allVersions = await this.getSolcVersions(); } catch (error) { - throw this.errors("noRequest", versionConstraint, error); + throw new NoRequestError(versionConstraint, error); } const isVersionRange = !semver.valid(versionConstraint); @@ -142,7 +143,7 @@ class VersionRange extends LoadingStrategy { : versionConstraint; const fileName = this.getSolcVersionFileName(versionToUse, allVersions); - if (!fileName) throw this.errors("noVersion", versionToUse); + if (!fileName) throw new NoVersionError(versionToUse); if (this.cache.fileIsCached(fileName)) return this.getCachedSolcByFileName(fileName); @@ -154,7 +155,7 @@ class VersionRange extends LoadingStrategy { events.emit("fetchSolcList:start", { attemptNumber: index + 1 }); if (!this.config.compilerRoots || this.config.compilerRoots.length < 1) { events.emit("fetchSolcList:fail"); - throw this.errors("noUrl"); + throw new NoUrlError(); } const { compilerRoots } = this.config; @@ -169,7 +170,7 @@ class VersionRange extends LoadingStrategy { .catch(error => { events.emit("fetchSolcList:fail"); if (index >= this.config.compilerRoots.length - 1) { - throw this.errors("noRequest", "version URLs", error); + throw new NoRequestError("version URLs", error); } return this.getSolcVersions(index + 1); }); @@ -227,4 +228,11 @@ class VersionRange extends LoadingStrategy { } } +class NoUrlError extends Error { + constructor() { + super("compiler root URL missing"); + } +} + + module.exports = VersionRange; From cd734fbf4b4c0f3e4424d86a720d314c88cb8c23 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 29 Jul 2021 19:30:05 -0400 Subject: [PATCH 07/35] Remove LoadingStrategy base class --- .../loadingStrategies/Docker.js | 11 ++++--- .../loadingStrategies/LoadingStrategy.js | 22 ------------- .../loadingStrategies/Local.js | 3 +- .../loadingStrategies/Native.js | 3 +- .../loadingStrategies/VersionRange.js | 14 +++++--- .../loadingStrategies/index.js | 1 - .../loadingStrategies/LoadingStrategy.js | 32 ------------------- 7 files changed, 19 insertions(+), 67 deletions(-) delete mode 100644 packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js delete mode 100644 packages/compile-solidity/test/compilerSupplier/loadingStrategies/LoadingStrategy.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index 79ab6f9ac2e..e3e00f6b255 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -3,14 +3,17 @@ const fs = require("fs"); const { execSync } = require("child_process"); const ora = require("ora"); const semver = require("semver"); -const LoadingStrategy = require("./LoadingStrategy"); const Cache = require("../Cache"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); const { NoVersionError, NoRequestError } = require("../errors"); -class Docker extends LoadingStrategy { - constructor(...args) { - super(...args); +class Docker { + constructor(options) { + const defaultConfig = { + dockerTagsUrl: + "https://registry.hub.docker.com/v2/repositories/ethereum/solc/tags/" + }; + this.config = Object.assign({}, defaultConfig, options); this.cache = new Cache(); } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js deleted file mode 100644 index 7d20c770d07..00000000000 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/LoadingStrategy.js +++ /dev/null @@ -1,22 +0,0 @@ -class LoadingStrategy { - constructor(options) { - const defaultConfig = { - compilerRoots: [ - "https://relay.trufflesuite.com/solc/bin/", - "https://solc-bin.ethereum.org/bin/", - "https://ethereum.github.io/solc-bin/bin/" - ], - dockerTagsUrl: - "https://registry.hub.docker.com/v2/repositories/ethereum/solc/tags/" - }; - this.config = Object.assign({}, defaultConfig, options); - } - - load(_userSpecification) { - throw new Error( - "Abstract method LoadingStrategy.load is not implemented for this strategy." - ); - } -}; - -module.exports = LoadingStrategy; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js index b7045646842..3ff4533c9aa 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js @@ -1,10 +1,9 @@ const path = require("path"); const originalRequire = require("original-require"); -const LoadingStrategy = require("./LoadingStrategy"); const solcWrap = require("solc/wrapper"); const observeListeners = require("../observeListeners"); -class Local extends LoadingStrategy { +class Local { load(localPath) { return this.getLocalCompiler(localPath); } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js index 2518c84323f..1d718aaf429 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js @@ -1,9 +1,8 @@ const { execSync } = require("child_process"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); -const LoadingStrategy = require("./LoadingStrategy"); const { NoVersionError } = require("../errors"); -class Native extends LoadingStrategy { +class Native { load() { const versionString = this.validateAndGetSolcVersion(); const command = "solc --standard-json"; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 5e7aa2bc26c..06859af7052 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -4,14 +4,20 @@ const originalRequire = require("original-require"); const axios = require("axios").default; const semver = require("semver"); const solcWrap = require("solc/wrapper"); -const LoadingStrategy = require("./LoadingStrategy"); const Cache = require("../Cache"); const observeListeners = require("../observeListeners"); const { NoVersionError, NoRequestError } = require("../errors"); -class VersionRange extends LoadingStrategy { - constructor(...args) { - super(...args); +class VersionRange { + constructor(options) { + const defaultConfig = { + compilerRoots: [ + "https://relay.trufflesuite.com/solc/bin/", + "https://solc-bin.ethereum.org/bin/", + "https://ethereum.github.io/solc-bin/bin/" + ] + }; + this.config = Object.assign({}, defaultConfig, options); this.cache = new Cache(); } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/index.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/index.js index 3fad53d268f..49c08d3c76a 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/index.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/index.js @@ -1,6 +1,5 @@ module.exports = { Docker: require("./Docker"), - LoadingStrategy: require("./LoadingStrategy"), Local: require("./Local"), Native: require("./Native"), VersionRange: require("./VersionRange") diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/LoadingStrategy.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/LoadingStrategy.js deleted file mode 100644 index 903beb1a935..00000000000 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/LoadingStrategy.js +++ /dev/null @@ -1,32 +0,0 @@ -const assert = require("assert"); -const { - LoadingStrategy -} = require("../../../compilerSupplier/loadingStrategies"); -let expectedDefaultConfig, loadingStrategyOptions; - -describe("LoadingStrategy base class", () => { - beforeEach(() => { - loadingStrategyOptions = { - version: null, - events: { - emit: () => {} - } - }; - instance = new LoadingStrategy(loadingStrategyOptions); - expectedDefaultConfig = { - compilerRoots: [ - "https://relay.trufflesuite.com/solc/bin/", - "https://solc-bin.ethereum.org/bin/", - "https://ethereum.github.io/solc-bin/bin/" - ], - dockerTagsUrl: - "https://registry.hub.docker.com/v2/repositories/ethereum/solc/tags/" - }; - }); - - it("has a config with some default values", () => { - const { compilerRoots, dockerTagsUrl } = instance.config; - assert.deepEqual(compilerRoots, expectedDefaultConfig.compilerRoots); - assert(dockerTagsUrl === expectedDefaultConfig.dockerTagsUrl); - }); -}); From aa1318a5e469109149e4448090333b88ac80470d Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 30 Jul 2021 19:13:49 -0400 Subject: [PATCH 08/35] Move public methods above private ones --- .../compilerSupplier/index.js | 70 +++++++++---------- .../loadingStrategies/VersionRange.js | 32 ++++----- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/index.js b/packages/compile-solidity/compilerSupplier/index.js index 8c333534940..3be38dac9a2 100644 --- a/packages/compile-solidity/compilerSupplier/index.js +++ b/packages/compile-solidity/compilerSupplier/index.js @@ -22,17 +22,6 @@ class CompilerSupplier { if (spawn) this.strategyOptions.spawn = spawn; } - badInputError(userSpecification) { - const message = - `Could not find a compiler version matching ${userSpecification}. ` + - `compilers.solc.version option must be a string specifying:\n` + - ` - a path to a locally installed solcjs\n` + - ` - a solc version or range (ex: '0.4.22' or '^0.5.0')\n` + - ` - a docker image name (ex: 'stable')\n` + - ` - 'native' to use natively installed solc\n`; - return new Error(message); - } - async load() { const userSpecification = this.version; @@ -62,30 +51,6 @@ class CompilerSupplier { } } - async loadParserSolc(parser, solc) { - if (parser) { - this.checkParser(parser); - const solcVersion = solc.version(); - const normalizedSolcVersion = semver.coerce(solcVersion).version; - const options = Object.assign({}, this.strategyOptions, { - version: normalizedSolcVersion - }); - return await new VersionRange(options).load(normalizedSolcVersion); - } - return false; - } - - checkParser(parser) { - if (parser !== "solcjs") - throw new Error( - `Unsupported parser "${parser}" found in truffle-config.js` - ); - } - - fileExists(localPath) { - return fs.existsSync(localPath) || path.isAbsolute(localPath); - } - getDockerTags() { return new Docker(this.strategyOptions).getDockerTags(); } @@ -113,6 +78,41 @@ class CompilerSupplier { static getDefaultVersion() { return defaultSolcVersion; } + + badInputError(userSpecification) { + const message = + `Could not find a compiler version matching ${userSpecification}. ` + + `compilers.solc.version option must be a string specifying:\n` + + ` - a path to a locally installed solcjs\n` + + ` - a solc version or range (ex: '0.4.22' or '^0.5.0')\n` + + ` - a docker image name (ex: 'stable')\n` + + ` - 'native' to use natively installed solc\n`; + return new Error(message); + } + + async loadParserSolc(parser, solc) { + if (parser) { + this.checkParser(parser); + const solcVersion = solc.version(); + const normalizedSolcVersion = semver.coerce(solcVersion).version; + const options = Object.assign({}, this.strategyOptions, { + version: normalizedSolcVersion + }); + return await new VersionRange(options).load(normalizedSolcVersion); + } + return false; + } + + checkParser(parser) { + if (parser !== "solcjs") + throw new Error( + `Unsupported parser "${parser}" found in truffle-config.js` + ); + } + + fileExists(localPath) { + return fs.existsSync(localPath) || path.isAbsolute(localPath); + } } module.exports = CompilerSupplier; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 06859af7052..70239686aac 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -22,6 +22,22 @@ class VersionRange { this.cache = new Cache(); } + async load(versionRange) { + const rangeIsSingleVersion = semver.valid(versionRange); + if (rangeIsSingleVersion && this.versionIsCached(versionRange)) { + return this.getCachedSolcByVersionRange(versionRange); + } + + try { + return await this.getSolcFromCacheOrUrl(versionRange); + } catch (error) { + if (error.message.includes("Failed to complete request")) { + return this.getSatisfyingVersionFromCache(versionRange); + } + throw new Error(error); + } + } + compilerFromString(code) { const listeners = observeListeners(); try { @@ -206,22 +222,6 @@ class VersionRange { return null; } - async load(versionRange) { - const rangeIsSingleVersion = semver.valid(versionRange); - if (rangeIsSingleVersion && this.versionIsCached(versionRange)) { - return this.getCachedSolcByVersionRange(versionRange); - } - - try { - return await this.getSolcFromCacheOrUrl(versionRange); - } catch (error) { - if (error.message.includes("Failed to complete request")) { - return this.getSatisfyingVersionFromCache(versionRange); - } - throw new Error(error); - } - } - versionIsCached(version) { const cachedCompilerFileNames = this.cache.getCachedFileNames(); const cachedVersions = cachedCompilerFileNames.map(fileName => { From 424eaf282099e806226ee94c69ce158bd97294d6 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 30 Jul 2021 20:16:54 -0400 Subject: [PATCH 09/35] Eliminate wonky dual-load in CompilerSupplier - Stop doing a separate `this.loadParserSolc()` inside `supplier.load()` - Remove `parserSolc` from the return value of `supplier.load()`, since the invoker of this method can just load a specific parser itself, and forcing this cleans up the CompilerSupplier interface / logic flow - Add the parserSolc selection logic to the only place in Truffle that uses the now-gone return value. (!! thus breaking @truffle/compile-solidity !!) --- .../compilerSupplier/index.js | 26 +--------- .../compile-solidity/profiler/loadParser.js | 50 +++++++++++++++---- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/index.js b/packages/compile-solidity/compilerSupplier/index.js index 3be38dac9a2..01c520c0092 100644 --- a/packages/compile-solidity/compilerSupplier/index.js +++ b/packages/compile-solidity/compilerSupplier/index.js @@ -8,9 +8,8 @@ const defaultSolcVersion = "0.5.16"; class CompilerSupplier { constructor({ events, solcConfig }) { - const { version, docker, compilerRoots, parser, spawn } = solcConfig; + const { version, docker, compilerRoots, spawn } = solcConfig; this.events = events; - this.parser = parser; this.version = version ? version : defaultSolcVersion; this.docker = docker; this.compilerRoots = compilerRoots; @@ -44,8 +43,7 @@ class CompilerSupplier { if (strategy) { const solc = await strategy.load(userSpecification); - const parserSolc = await this.loadParserSolc(this.parser, solc); - return { solc, parserSolc }; + return { solc }; } else { throw this.badInputError(userSpecification); } @@ -90,26 +88,6 @@ class CompilerSupplier { return new Error(message); } - async loadParserSolc(parser, solc) { - if (parser) { - this.checkParser(parser); - const solcVersion = solc.version(); - const normalizedSolcVersion = semver.coerce(solcVersion).version; - const options = Object.assign({}, this.strategyOptions, { - version: normalizedSolcVersion - }); - return await new VersionRange(options).load(normalizedSolcVersion); - } - return false; - } - - checkParser(parser) { - if (parser !== "solcjs") - throw new Error( - `Unsupported parser "${parser}" found in truffle-config.js` - ); - } - fileExists(localPath) { return fs.existsSync(localPath) || path.isAbsolute(localPath); } diff --git a/packages/compile-solidity/profiler/loadParser.js b/packages/compile-solidity/profiler/loadParser.js index b0a5e4e8343..bf9774c314a 100644 --- a/packages/compile-solidity/profiler/loadParser.js +++ b/packages/compile-solidity/profiler/loadParser.js @@ -2,20 +2,48 @@ const CompilerSupplier = require("../compilerSupplier"); const Parser = require("../parser"); const semver = require("semver"); -async function loadParser(options) { - // Load compiler - const supplierOptions = { - parser: options.parser, - events: options.events, - solcConfig: options.compilers.solc - }; +/** + * Loads solc and wrap it to parse imports rather than performing a full + * compilation. Returns the wrapped form. + * + * This function optionally accepts an `parser` param, whose only possible + * value is `"solcjs"`. Passing this option indicates that the imports-parser + * should use a wrapped soljson module instead of whatever normal compiler + * the user would use. NOTE that as a result, this function may download solc + * up to twice: first time as usual, to get the specific version, then a second + * time to get the solcjs of that version. + */ +async function loadParser({ events, compilers: { solc: solcConfig } }) { + const { parser } = solcConfig; + + const supplier = new CompilerSupplier({ events, solcConfig }); + const { solc } = await supplier.load(); - const supplier = new CompilerSupplier(supplierOptions); + // if no parser is specified, just use the normal solc + if (!parser) { + return makeParseImports(solc); + } - const { solc, parserSolc } = await supplier.load(); + // otherwise, there's only one choice... + if (parser !== "solcjs") { + throw new Error( + `Unsupported parser "${parser}" found in truffle-config.js` + ); + } + + // determine normal solc version and then load that version as solcjs + const { version } = semver.coerce(solc.version()); + const parserSupplier = new CompilerSupplier({ + events, + solcConfig: { + ...solcConfig, + version, + docker: false + } + }); + const { solc: parserSolc } = await parserSupplier.load(); - // use explicit parser solc if defined, otherwise just default compiler solc - return makeParseImports(parserSolc || solc); + return makeParseImports(parserSolc); } function makeParseImports(parser) { From 883364238e129f945a7b6ac4ae87b72ffea7ede6 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 30 Jul 2021 22:19:42 -0400 Subject: [PATCH 10/35] Normalize supplier.list() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Define new unified method for listing versions, which returns { latestRelease, prereleases, releases}, where `prereleases` and `releases` may both be AsyncIterableIterators - Align VersionRange with new interface for this method - Replace naïve `docker.getDockerTags()`, which only returns the first page of results from the Hub API: - Implement API page traversal as an AsyncIterableIterator of all tags individually. - Add retry logic to axios to perform exponential backoff to get around Docker Hub's 429 Too Many Requests responses - Use iter-tools to fork/filter/conform unified stream into `{ latestRelease, prereleases, releases }` - Add warning not to do `truffle compile --list=docker --all` --- .../compileWithPragmaAnalysis.js | 7 +- .../compilerSupplier/index.js | 51 ++++++----- .../loadingStrategies/Docker.js | 89 +++++++++++++++++-- .../loadingStrategies/VersionRange.js | 18 ++++ packages/compile-solidity/package.json | 2 + .../test/compileWithPragmaAnalysis.js | 4 +- packages/core/lib/commands/compile.js | 56 ++++++++---- packages/core/test/lib/commands/compile.js | 10 +-- yarn.lock | 7 ++ 9 files changed, 190 insertions(+), 54 deletions(-) diff --git a/packages/compile-solidity/compileWithPragmaAnalysis.js b/packages/compile-solidity/compileWithPragmaAnalysis.js index 741e88f5fd5..a1bd09ad387 100644 --- a/packages/compile-solidity/compileWithPragmaAnalysis.js +++ b/packages/compile-solidity/compileWithPragmaAnalysis.js @@ -67,10 +67,13 @@ const compileWithPragmaAnalysis = async ({ paths, options }) => { } const supplierOptions = { events: options.events, - solcConfig: options.compilers.solc + solcConfig: { + ...options.compilers.solc, + docker: false + } }; const compilerSupplier = new CompilerSupplier(supplierOptions); - const { releases } = await compilerSupplier.getReleases(); + const { releases } = await compilerSupplier.list(); // collect sources by the version of the Solidity compiler that they require const versionsAndSources = {}; diff --git a/packages/compile-solidity/compilerSupplier/index.js b/packages/compile-solidity/compilerSupplier/index.js index 01c520c0092..6ab0392c4d1 100644 --- a/packages/compile-solidity/compilerSupplier/index.js +++ b/packages/compile-solidity/compilerSupplier/index.js @@ -49,28 +49,37 @@ class CompilerSupplier { } } - getDockerTags() { - return new Docker(this.strategyOptions).getDockerTags(); - } + async list() { + const userSpecification = this.version; + + let strategy; + const useDocker = this.docker; + const useNative = userSpecification === "native"; + const useSpecifiedLocal = + userSpecification && this.fileExists(userSpecification); + const isValidVersionRange = semver.validRange(userSpecification); + + if (useDocker) { + strategy = new Docker(this.strategyOptions); + } else if (useNative) { + strategy = new Native(this.strategyOptions); + } else if (useSpecifiedLocal) { + strategy = new Local(this.strategyOptions); + } else if (isValidVersionRange) { + strategy = new VersionRange(this.strategyOptions); + } + + if (!strategy) { + throw this.badInputError(userSpecification); + } + + if (!strategy.list) { + throw new Error( + `Cannot list versions for strategy ${strategy.constructor.name}` + ); + } - getReleases() { - return new VersionRange(this.strategyOptions) - .getSolcVersions() - .then(list => { - const prereleases = list.builds - .filter(build => build["prerelease"]) - .map(build => build["longVersion"]); - - const { rsort } = semver; - // ensure releases are listed in descending order - const releases = rsort(Object.keys(list.releases)); - - return { - prereleases: prereleases, - releases: releases, - latestRelease: list.latestRelease - }; - }); + return await strategy.list(); } static getDefaultVersion() { diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index e3e00f6b255..ae604588809 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -1,4 +1,5 @@ const axios = require("axios"); +const axiosRetry = require("axios-retry"); const fs = require("fs"); const { execSync } = require("child_process"); const ora = require("ora"); @@ -6,6 +7,7 @@ const semver = require("semver"); const Cache = require("../Cache"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); const { NoVersionError, NoRequestError } = require("../errors"); +const { asyncFirst, asyncFilter, asyncFork } = require("iter-tools"); class Docker { constructor(options) { @@ -46,14 +48,39 @@ class Docker { } } - getDockerTags() { - return axios.get(this.config.dockerTagsUrl, { maxRedirects: 50 }) - .then(response => response.data.results.map(item => item.name)) - .catch(error => { - throw new NoRequestError(this.config.dockerTagsUrl, error); - }); + async list() { + const allTags = this.streamAllDockerTags(); + + // split stream of all tags into separate releases and prereleases streams + const isRelease = name => !!semver.valid(name); + const isPrerelease = name => name.match(/nightly/); + const [allTagsA, allTagsB] = asyncFork(allTags); + + // construct prereleases stream + const prereleases = asyncFilter(isPrerelease, allTagsB); + + // construct releases stream and immediately fork so as to allow consuming + // the first value in the stream safely + const [releases, forkedReleases] = asyncFork( + asyncFilter(isRelease, allTagsA) + ); + + // grab the latest release from the forked releases stream; + // coerce semver to remove possible `-alpine` suffix used by this repo + const latestRelease = semver.coerce(await asyncFirst(forkedReleases)) + .version; + + return { + prereleases, + releases, + latestRelease + }; } + /* + * Private methods + */ + downloadDockerImage(image) { if (!semver.valid(image)) { const message = @@ -106,12 +133,56 @@ class Docker { const version = execSync( "docker run ethereum/solc:" + image + " --version" ); - const normalized = normalizeSolcVersion( - version - ); + const normalized = normalizeSolcVersion(version); this.cache.addFileToCache(normalized, fileName); return normalized; } + + streamAllDockerTags() { + // build http client to account for rate limit problems + // use axiosRetry to instate exponential backoff when requests come back + // with expected 429 + const client = axios.create(); + axiosRetry(client, { + retries: 5, + retryDelay: axiosRetry.exponentialDelay, + shouldResetTimeout: true, + retryCondition: error => { + const tooManyRequests = + error && error.response && error.response.status === 429; + + return ( + axiosRetry.isNetworkOrIdempotentRequestError(error) || tooManyRequests + ); + } + }); + + const { dockerTagsUrl } = this.config; + let nextUrl = dockerTagsUrl; + + return (async function* () { + do { + try { + const { + data: { + // page of results + results, + // next page url + next + } + } = await client.get(nextUrl, { maxRedirects: 50 }); + + for (const { name } of results) { + yield name; + } + + nextUrl = next; + } catch (error) { + throw new NoRequestError(dockerTagsUrl, error); + } + } while (nextUrl); + })(); + } } class NoDockerError extends Error { diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 70239686aac..040f6e9777a 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -38,6 +38,24 @@ class VersionRange { } } + async list() { + const data = await this.getSolcVersions(); + const { latestRelease } = data; + + const prereleases = data.builds + .filter(build => build["prerelease"]) + .map(build => build["longVersion"]); + + // ensure releases are listed in descending order + const releases = semver.rsort(Object.keys(data.releases)); + + return { + prereleases, + releases, + latestRelease + }; + } + compilerFromString(code) { const listeners = observeListeners(); try { diff --git a/packages/compile-solidity/package.json b/packages/compile-solidity/package.json index 1900bf73ce5..872f5113476 100644 --- a/packages/compile-solidity/package.json +++ b/packages/compile-solidity/package.json @@ -24,8 +24,10 @@ "@truffle/contract-sources": "^0.1.12", "@truffle/expect": "^0.0.18", "axios": "^0.21.1", + "axios-retry": "^3.1.9", "debug": "^4.3.1", "fs-extra": "^9.1.0", + "iter-tools": "^7.0.2", "lodash.clonedeep": "^4.5.0", "lodash.partition": "^4.6.0", "ora": "^3.4.0", diff --git a/packages/compile-solidity/test/compileWithPragmaAnalysis.js b/packages/compile-solidity/test/compileWithPragmaAnalysis.js index 00562e3e89e..9dba380da26 100644 --- a/packages/compile-solidity/test/compileWithPragmaAnalysis.js +++ b/packages/compile-solidity/test/compileWithPragmaAnalysis.js @@ -74,10 +74,10 @@ config.resolver = new Resolver(config); describe("compileWithPragmaAnalysis", function () { before(function () { - sinon.stub(CompilerSupplier.prototype, "getReleases").returns(releases); + sinon.stub(CompilerSupplier.prototype, "list").returns(releases); }); after(function () { - CompilerSupplier.prototype.getReleases.restore(); + CompilerSupplier.prototype.list.restore(); }); describe("solidity files with no imports", function () { diff --git a/packages/core/lib/commands/compile.js b/packages/core/lib/commands/compile.js index e2eb1b1186a..9f699cc3357 100644 --- a/packages/core/lib/commands/compile.js +++ b/packages/core/lib/commands/compile.js @@ -23,8 +23,7 @@ const command = { } }, help: { - usage: - "truffle compile [--list ] [--all] [--quiet]", + usage: "truffle compile [--list ] [--all] [--quiet]", options: [ { option: "--all", @@ -52,7 +51,7 @@ const command = { internal: true, description: "Save the raw compiler results into , overwriting any existing content." - }, + } ], allowedGlobalOptions: ["config"] }, @@ -86,7 +85,7 @@ const command = { await fse.writeFile( compilationOutputFile, JSON.stringify(compilationOutput), - {encoding: "utf8"} + { encoding: "utf8" } ); } @@ -98,28 +97,55 @@ const command = { }, listVersions: async function (options) { - const {CompilerSupplier} = require("@truffle/compile-solidity"); + const { CompilerSupplier } = require("@truffle/compile-solidity"); + const { asyncTake } = require("iter-tools"); + const supplier = new CompilerSupplier({ - solcConfig: options.compilers.solc, + solcConfig: { + ...options.compilers.solc, + docker: options.list === "docker" + }, events: options.events }); const log = options.logger.log; options.list = options.list.length ? options.list : "releases"; + const { + latestRelease, + releases, + prereleases + } = await supplier.list(); + if (options.list === "latestRelease") { + log(format(latestRelease, null, " ")); + return; + } + + const allVersions = options.list === "prereleases" ? prereleases : releases; + const versions = options.all ? allVersions : asyncTake(10, allVersions); + + if (options.all && options.list === "docker") { + log( + "Warning: using `--all` with `--list=docker` is very slow and makes " + + "many HTTP requests." + ); + log( + "You may instead want to browse tags on the web here: " + + "https://hub.docker.com/r/ethereum/solc/tags/" + ); + } + + const tags = []; + for await (const version of versions) { + tags.push(version); + } + // Docker tags - if (options.list === "docker") { - const tags = await supplier.getDockerTags(); + if (options.list === "docker" && !options.all) { tags.push("See more at: hub.docker.com/r/ethereum/solc/tags/"); - log(format(tags, null, " ")); - return; } - // Solcjs releases - const releases = await supplier.getReleases(); - const shortener = options.all ? null : command.shortener; - const list = format(releases[options.list], shortener, " "); - log(list); + log(format(tags, null, " ")); return; }, diff --git a/packages/core/test/lib/commands/compile.js b/packages/core/test/lib/commands/compile.js index f80248ea21b..f37a2ffa8b5 100644 --- a/packages/core/test/lib/commands/compile.js +++ b/packages/core/test/lib/commands/compile.js @@ -26,7 +26,7 @@ describe("compile", function () { } }; config.network = "default"; - config.logger = {log: val => val && memStream.write(val)}; + config.logger = { log: val => val && memStream.write(val) }; }); after("Cleanup tmp files", async function () { @@ -37,7 +37,7 @@ describe("compile", function () { afterEach("Clear MemoryStream", () => (output = "")); it("compiles all initial contracts", async function () { - const {contracts} = await WorkflowCompile.compileAndSave( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true @@ -51,7 +51,7 @@ describe("compile", function () { }); it("compiles no contracts after no updates", async function () { - const {contracts} = await WorkflowCompile.compileAndSave( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true @@ -74,7 +74,7 @@ describe("compile", function () { const newTime = new Date().getTime(); fs.utimesSync(fileToUpdate, newTime, newTime); - const {contracts} = await WorkflowCompile.compileAndSave( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true @@ -115,7 +115,7 @@ describe("compile", function () { await command.run(config.with(options)); memStream.on("end", () => { const arr = JSON.parse(output); - assert(arr.length === 11); + assert(arr.length === 10); }); memStream.end(""); }); diff --git a/yarn.lock b/yarn.lock index c78ca963c99..17b08cc853f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6391,6 +6391,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" integrity sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A== +axios-retry@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.1.9.tgz#6c30fc9aeb4519aebaec758b90ef56fa03fe72e8" + integrity sha512-NFCoNIHq8lYkJa6ku4m+V1837TP6lCa7n79Iuf8/AqATAHYB0ISaAS1eyIenDOfHOLtym34W65Sjke2xjg2fsA== + dependencies: + is-retry-allowed "^1.1.0" + axios@^0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" From 317a12d5a32a680daefd20ea1268e746762eba7e Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 19:05:47 -0400 Subject: [PATCH 11/35] Rename Cache methods - Rename cache.fileIsCached() to cache.has() - Rename cache.addFileToCache() to cache.add() - Rename cache.resolveCache() to cache.resolve() - Rename cache.getCachedFileNames() to cache.list() --- .../compilerSupplier/Cache.js | 12 +++---- .../loadingStrategies/Docker.js | 6 ++-- .../loadingStrategies/VersionRange.js | 12 +++---- .../test/compilerSupplier/index.js | 8 ++--- .../loadingStrategies/VersionRange.js | 32 +++++++++---------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/Cache.js b/packages/compile-solidity/compilerSupplier/Cache.js index 97be6887dd1..e5659c58d9f 100644 --- a/packages/compile-solidity/compilerSupplier/Cache.js +++ b/packages/compile-solidity/compilerSupplier/Cache.js @@ -15,21 +15,21 @@ class Cache { this.compilerCachePath = compilerCachePath; } - getCachedFileNames() { + list() { return fs.readdirSync(this.compilerCachePath); } - addFileToCache(code, fileName) { - const filePath = this.resolveCache(fileName); + add(code, fileName) { + const filePath = this.resolve(fileName); fs.writeFileSync(filePath, code); } - fileIsCached(fileName) { - const file = this.resolveCache(fileName); + has(fileName) { + const file = this.resolve(fileName); return fs.existsSync(file); } - resolveCache(fileName) { + resolve(fileName) { return path.resolve(this.compilerCachePath, fileName); } }; diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index ae604588809..f2c3cfea7d4 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -106,8 +106,8 @@ class Docker { const fileName = image + ".version"; // Skip validation if they've validated for this image before. - if (this.cache.fileIsCached(fileName)) { - const cachePath = this.cache.resolveCache(fileName); + if (this.cache.has(fileName)) { + const cachePath = this.cache.resolve(fileName); return fs.readFileSync(cachePath, "utf-8"); } // Image specified @@ -134,7 +134,7 @@ class Docker { "docker run ethereum/solc:" + image + " --version" ); const normalized = normalizeSolcVersion(version); - this.cache.addFileToCache(normalized, fileName); + this.cache.add(normalized, fileName); return normalized; } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 040f6e9777a..ec3dda658a1 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -85,7 +85,7 @@ class VersionRange { getCachedSolcByFileName(fileName) { const listeners = observeListeners(); try { - const filePath = this.cache.resolveCache(fileName); + const filePath = this.cache.resolve(fileName); const soljson = originalRequire(filePath); debug("soljson %o", soljson); return solcWrap(soljson); @@ -96,7 +96,7 @@ class VersionRange { // Range can also be a single version specification like "0.5.0" getCachedSolcByVersionRange(version) { - const cachedCompilerFileNames = this.cache.getCachedFileNames(); + const cachedCompilerFileNames = this.cache.list(); const validVersions = cachedCompilerFileNames.filter(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return semver.satisfies(match[0], version); @@ -110,7 +110,7 @@ class VersionRange { } getCachedSolcFileName(commit) { - const cachedCompilerFileNames = this.cache.getCachedFileNames(); + const cachedCompilerFileNames = this.cache.list(); return cachedCompilerFileNames.find(fileName => { return fileName.includes(commit); }); @@ -158,7 +158,7 @@ class VersionRange { try { const response = await axios.get(url, { maxRedirects: 50 }); events.emit("downloadCompiler:succeed"); - this.cache.addFileToCache(response.data, fileName); + this.cache.add(response.data, fileName); return this.compilerFromString(response.data); } catch (error) { events.emit("downloadCompiler:fail"); @@ -185,7 +185,7 @@ class VersionRange { if (!fileName) throw new NoVersionError(versionToUse); - if (this.cache.fileIsCached(fileName)) + if (this.cache.has(fileName)) return this.getCachedSolcByFileName(fileName); return this.getSolcByUrlAndCache(fileName); } @@ -241,7 +241,7 @@ class VersionRange { } versionIsCached(version) { - const cachedCompilerFileNames = this.cache.getCachedFileNames(); + const cachedCompilerFileNames = this.cache.list(); const cachedVersions = cachedCompilerFileNames.map(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return match[0]; diff --git a/packages/compile-solidity/test/compilerSupplier/index.js b/packages/compile-solidity/test/compilerSupplier/index.js index 00220486ab1..0efbcba647d 100644 --- a/packages/compile-solidity/test/compilerSupplier/index.js +++ b/packages/compile-solidity/test/compilerSupplier/index.js @@ -137,8 +137,8 @@ describe("CompilerSupplier", () => { describe("when a user specifies the compiler url root", () => { beforeEach(() => { - sinon.stub(Cache.prototype, "addFileToCache"); - sinon.stub(Cache.prototype, "fileIsCached").returns(false); + sinon.stub(Cache.prototype, "add"); + sinon.stub(Cache.prototype, "has").returns(false); sinon.stub(VersionRange.prototype, "getSolcVersions") .returns(allVersions); sinon.stub(VersionRange.prototype, "versionIsCached").returns(false); @@ -150,8 +150,8 @@ describe("CompilerSupplier", () => { .returns({ data: "response" }); }); afterEach(() => { - Cache.prototype.addFileToCache.restore(); - Cache.prototype.fileIsCached.restore(); + Cache.prototype.add.restore(); + Cache.prototype.has.restore(); VersionRange.prototype.getSolcVersions.restore(); VersionRange.prototype.versionIsCached.restore(); VersionRange.prototype.compilerFromString.restore(); diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js index 7cab88a7f38..2d40836bac9 100644 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js @@ -97,11 +97,11 @@ describe("VersionRange loading strategy", () => { describe("when a version constraint is specified", () => { beforeEach(() => { sinon.stub(instance, "getSolcByUrlAndCache"); - sinon.stub(instance.cache, "fileIsCached").returns(false); + sinon.stub(instance.cache, "has").returns(false); }); afterEach(() => { instance.getSolcByUrlAndCache.restore(); - instance.cache.fileIsCached.restore(); + instance.cache.has.restore(); }); it("calls findNewstValidVersion to determine which version to fetch", async () => { @@ -117,10 +117,10 @@ describe("VersionRange loading strategy", () => { describe("when the version is cached", () => { beforeEach(() => { - sinon.stub(instance.cache, "fileIsCached").returns(true); + sinon.stub(instance.cache, "has").returns(true); }); afterEach(() => { - instance.cache.fileIsCached.restore(); + instance.cache.has.restore(); }); it("calls getCachedSolcByFileName", async () => { @@ -135,19 +135,19 @@ describe("VersionRange loading strategy", () => { describe("when the version is not cached", () => { beforeEach(() => { - sinon.stub(instance.cache, "fileIsCached").returns(false); - sinon.stub(instance.cache, "addFileToCache"); + sinon.stub(instance.cache, "has").returns(false); + sinon.stub(instance.cache, "add"); sinon.stub(instance, "compilerFromString").returns("compiler"); }); afterEach(() => { - instance.cache.fileIsCached.restore(); - instance.cache.addFileToCache.restore(); + instance.cache.has.restore(); + instance.cache.add.restore(); instance.compilerFromString.restore(); }); - it("eventually calls addFileToCache and compilerFromString", async () => { + it("eventually calls add and compilerFromString", async () => { await instance.getSolcFromCacheOrUrl("0.5.1"); - assert(instance.cache.addFileToCache.called); + assert(instance.cache.add.called); assert(instance.compilerFromString.called); }).timeout(60000); }); @@ -184,13 +184,13 @@ describe("VersionRange loading strategy", () => { .returns(undefined); sinon.stub(axios, "get") .returns(Promise.resolve({ data: "the stuff" })); - sinon.stub(instance.cache, "addFileToCache"); + sinon.stub(instance.cache, "add"); sinon.stub(instance, "compilerFromString"); }); afterEach(() => { instance.getCachedSolcFileName.restore(); axios.get.restore(); - instance.cache.addFileToCache.restore(); + instance.cache.add.restore(); instance.compilerFromString.restore(); }); @@ -216,7 +216,7 @@ describe("VersionRange loading strategy", () => { .stub(axios, "get") .withArgs(`${instance.config.compilerRoots[0]}${fileName}`) .returns({ data: "requestReturn" }); - sinon.stub(instance.cache, "addFileToCache").withArgs("requestReturn"); + sinon.stub(instance.cache, "add").withArgs("requestReturn"); sinon .stub(instance, "compilerFromString") .withArgs("requestReturn") @@ -224,14 +224,14 @@ describe("VersionRange loading strategy", () => { }); afterEach(() => { axios.get.restore(); - instance.cache.addFileToCache.restore(); + instance.cache.add.restore(); instance.compilerFromString.restore(); }); - it("calls addFileToCache with the response and the file name", async () => { + it("calls add with the response and the file name", async () => { const result = await instance.getSolcByUrlAndCache(fileName, 0); assert( - instance.cache.addFileToCache.calledWith("requestReturn", "someSolcFile") + instance.cache.add.calledWith("requestReturn", "someSolcFile") ); assert(result === "success"); }); From 615e5a7bcee542140145c85cf063fcffc7816b0b Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 19:23:58 -0400 Subject: [PATCH 12/35] Add suggested comments --- packages/compile-solidity/compileWithPragmaAnalysis.js | 3 +++ .../compilerSupplier/loadingStrategies/Docker.js | 8 ++++++++ .../compilerSupplier/loadingStrategies/VersionRange.js | 5 +++++ .../compilerSupplier/normalizeSolcVersion.js | 4 ++++ packages/core/lib/commands/compile.js | 3 ++- 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/compile-solidity/compileWithPragmaAnalysis.js b/packages/compile-solidity/compileWithPragmaAnalysis.js index a1bd09ad387..5bef16c4bfe 100644 --- a/packages/compile-solidity/compileWithPragmaAnalysis.js +++ b/packages/compile-solidity/compileWithPragmaAnalysis.js @@ -65,6 +65,9 @@ const compileWithPragmaAnalysis = async ({ paths, options }) => { if (filteredPaths.length === 0) { return { compilations: [] }; } + // construct supplier options for fetching list of solc versions; + // enforce no Docker because listing Docker versions is slow (Docker Hub API + // paginates responses, with >500 pages at time of writing) const supplierOptions = { events: options.events, solcConfig: { diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index f2c3cfea7d4..df57426e570 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -48,6 +48,14 @@ class Docker { } } + /** + * Fetch list of solc versions available as Docker images. + * + * This returns a promise for an object with three fields: + * { latestRelease, releases, prereleases } + * NOTE that `releases` and `prereleases` in this object are both + * AsyncIterableIterators (thus, use only `for await (const ...)` to consume) + */ async list() { const allTags = this.streamAllDockerTags(); diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index ec3dda658a1..2a4f79c4d4b 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -12,6 +12,11 @@ class VersionRange { constructor(options) { const defaultConfig = { compilerRoots: [ + // NOTE this relay address exists so that we have a backup option in + // case more official distribution mechanisms fail. + // + // currently this URL just redirects (302 Found); we may alter this to + // host for real in the future. "https://relay.trufflesuite.com/solc/bin/", "https://solc-bin.ethereum.org/bin/", "https://ethereum.github.io/solc-bin/bin/" diff --git a/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js b/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js index 68c23605747..45699a75684 100644 --- a/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js +++ b/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js @@ -1,3 +1,7 @@ +/** + * Convert a full version string (possibly with commit information, etc.) into + * a canonical short-form semver version (x.y.z) + */ const normalizeSolcVersion = (input) => { const version = String(input); return version.split(":")[1].trim(); diff --git a/packages/core/lib/commands/compile.js b/packages/core/lib/commands/compile.js index 9f699cc3357..76e15942b33 100644 --- a/packages/core/lib/commands/compile.js +++ b/packages/core/lib/commands/compile.js @@ -136,11 +136,12 @@ const command = { } const tags = []; + // use `for await` because Docker strategy returns AsyncIterableIterators for await (const version of versions) { tags.push(version); } - // Docker tags + // Docker tags are best browsed via their own web UI if (options.list === "docker" && !options.all) { tags.push("See more at: hub.docker.com/r/ethereum/solc/tags/"); } From cb8decdfe1e6bc4589b0293978845e120aa3a9cb Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 19:30:24 -0400 Subject: [PATCH 13/35] Remove extra method in Local strategy --- .../compilerSupplier/loadingStrategies/Local.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js index 3ff4533c9aa..841a91cb256 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js @@ -5,10 +5,6 @@ const observeListeners = require("../observeListeners"); class Local { load(localPath) { - return this.getLocalCompiler(localPath); - } - - getLocalCompiler(localPath) { const listeners = observeListeners(); try { let soljson, compilerPath; From 2a51b86fdc27fe5f55ffabe9f6f02d8ffcc5e5a9 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 19:39:29 -0400 Subject: [PATCH 14/35] Rethrow caught errors instead of making new ones --- .../compilerSupplier/loadingStrategies/Docker.js | 6 ++---- .../compilerSupplier/loadingStrategies/Native.js | 2 +- .../compilerSupplier/loadingStrategies/VersionRange.js | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js index df57426e570..414ac2b67da 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js @@ -44,7 +44,7 @@ class Docker { if (error.message === "No matching version found") { throw new NoVersionError(versionString); } - throw new Error(error); + throw error; } } @@ -102,10 +102,8 @@ class Docker { }).start(); try { execSync(`docker pull ethereum/solc:${image}`); + } finally { spinner.stop(); - } catch (error) { - spinner.stop(); - throw new Error(error); } } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js index 1d718aaf429..f9f5fdfc78d 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js @@ -18,7 +18,7 @@ class Native { if (error.message === "No matching version found") { throw new NoVersionError(versionString); } - throw new Error(error); + throw error; } } diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 2a4f79c4d4b..b82e0d95a40 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -39,7 +39,7 @@ class VersionRange { if (error.message.includes("Failed to complete request")) { return this.getSatisfyingVersionFromCache(versionRange); } - throw new Error(error); + throw error; } } From 1d1b6c69f2744967336339234bbb77f2325df3be Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 19:47:00 -0400 Subject: [PATCH 15/35] Remove weird JSON.stringify rename --- packages/core/lib/commands/compile.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/lib/commands/compile.js b/packages/core/lib/commands/compile.js index 76e15942b33..8912bc0fb4c 100644 --- a/packages/core/lib/commands/compile.js +++ b/packages/core/lib/commands/compile.js @@ -1,4 +1,3 @@ -const format = JSON.stringify; const path = require("path"); const fse = require("fs-extra"); @@ -117,7 +116,7 @@ const command = { prereleases } = await supplier.list(); if (options.list === "latestRelease") { - log(format(latestRelease, null, " ")); + log(JSON.stringify(latestRelease, null, " ")); return; } @@ -146,7 +145,7 @@ const command = { tags.push("See more at: hub.docker.com/r/ethereum/solc/tags/"); } - log(format(tags, null, " ")); + log(JSON.stringify(tags, null, " ")); return; }, From e1752dae514e755e8910556721ed94557b158838 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 20:12:39 -0400 Subject: [PATCH 16/35] Clarify method name for getting+caching Rename `getSolcByUrlAndCache` to `getAndCacheSolcByUrl` --- .../loadingStrategies/VersionRange.js | 8 ++++---- .../loadingStrategies/VersionRange.js | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index b82e0d95a40..5085940670b 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -148,10 +148,10 @@ class VersionRange { const fileName = this.getSolcVersionFileName(commit, allVersions); if (!fileName) throw new Error("No matching version found"); - return this.getSolcByUrlAndCache(fileName); + return this.getAndCacheSolcByUrl(fileName); } - async getSolcByUrlAndCache(fileName, index = 0) { + async getAndCacheSolcByUrl(fileName, index = 0) { const url = `${this.config.compilerRoots[index].replace( /\/+$/, "" @@ -170,7 +170,7 @@ class VersionRange { if (index >= this.config.compilerRoots.length - 1) { throw new NoRequestError("compiler URLs", error); } - return this.getSolcByUrlAndCache(fileName, index + 1); + return this.getAndCacheSolcByUrl(fileName, index + 1); } } @@ -192,7 +192,7 @@ class VersionRange { if (this.cache.has(fileName)) return this.getCachedSolcByFileName(fileName); - return this.getSolcByUrlAndCache(fileName); + return this.getAndCacheSolcByUrl(fileName); } getSolcVersions(index = 0) { diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js index 2d40836bac9..c5e73515cfe 100644 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js @@ -96,21 +96,21 @@ describe("VersionRange loading strategy", () => { describe("when a version constraint is specified", () => { beforeEach(() => { - sinon.stub(instance, "getSolcByUrlAndCache"); + sinon.stub(instance, "getAndCacheSolcByUrl"); sinon.stub(instance.cache, "has").returns(false); }); afterEach(() => { - instance.getSolcByUrlAndCache.restore(); + instance.getAndCacheSolcByUrl.restore(); instance.cache.has.restore(); }); it("calls findNewstValidVersion to determine which version to fetch", async () => { await instance.getSolcFromCacheOrUrl("^0.5.0"); assert( - instance.getSolcByUrlAndCache.calledWith( + instance.getAndCacheSolcByUrl.calledWith( "soljson-v0.5.4+commit.9549d8ff.js" ), - "getSolcByUrlAndCache not called with the compiler file name" + "getAndCacheSolcByUrl not called with the compiler file name" ); }); }); @@ -209,7 +209,7 @@ describe("VersionRange loading strategy", () => { }); }); - describe(".getSolcByUrlAndCache(fileName)", () => { + describe(".getAndCacheSolcByUrl(fileName)", () => { beforeEach(() => { fileName = "someSolcFile"; sinon @@ -229,7 +229,7 @@ describe("VersionRange loading strategy", () => { }); it("calls add with the response and the file name", async () => { - const result = await instance.getSolcByUrlAndCache(fileName, 0); + const result = await instance.getAndCacheSolcByUrl(fileName, 0); assert( instance.cache.add.calledWith("requestReturn", "someSolcFile") ); From 1411d3b0130b03a3db10643f3e68b66d10fa0edb Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 20:14:42 -0400 Subject: [PATCH 17/35] Remove unused getSolcByCommit() --- .../loadingStrategies/VersionRange.js | 11 ---- .../loadingStrategies/VersionRange.js | 56 ------------------- 2 files changed, 67 deletions(-) diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js index 5085940670b..750ca643525 100644 --- a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js @@ -140,17 +140,6 @@ class VersionRange { throw new NoVersionError(versionRange); } - async getSolcByCommit(commit) { - const solcFileName = this.getCachedSolcFileName(commit); - if (solcFileName) return this.getCachedSolcByFileName(solcFileName); - - const allVersions = await this.getSolcVersions(); - const fileName = this.getSolcVersionFileName(commit, allVersions); - - if (!fileName) throw new Error("No matching version found"); - return this.getAndCacheSolcByUrl(fileName); - } - async getAndCacheSolcByUrl(fileName, index = 0) { const url = `${this.config.compilerRoots[index].replace( /\/+$/, diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js index c5e73515cfe..bf5e6ca64a9 100644 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js @@ -153,62 +153,6 @@ describe("VersionRange loading strategy", () => { }); }); - describe("async getSolcByCommit(commit)", () => { - describe("when the file is cached", () => { - beforeEach(() => { - sinon - .stub(instance, "getCachedSolcFileName") - .withArgs("commit.porkBelly") - .returns("someFile.js"); - sinon.stub(instance, "getCachedSolcByFileName"); - }); - afterEach(() => { - instance.getCachedSolcFileName.restore(); - instance.getCachedSolcByFileName.restore(); - }); - - it("calls getCachedSolcByFileName with the file name", async () => { - await instance.getSolcByCommit("commit.porkBelly"); - assert(instance.getCachedSolcByFileName.calledWith("someFile.js")); - }); - }); - - describe("when the file is not cached", () => { - let commitString; - beforeEach(() => { - // commit string for v 0.5.1 - commitString = "commit.c8a2cb62"; - sinon - .stub(instance, "getCachedSolcFileName") - .withArgs(commitString) - .returns(undefined); - sinon.stub(axios, "get") - .returns(Promise.resolve({ data: "the stuff" })); - sinon.stub(instance.cache, "add"); - sinon.stub(instance, "compilerFromString"); - }); - afterEach(() => { - instance.getCachedSolcFileName.restore(); - axios.get.restore(); - instance.cache.add.restore(); - instance.compilerFromString.restore(); - }); - - it("eventually calls compilerFromString with request reponse", async () => { - await instance.getSolcByCommit(commitString); - assert(instance.compilerFromString.calledWith("the stuff")); - }).timeout(20000); - it("throws an error when it can't find a match", async () => { - try { - await instance.getSolcByCommit("some garbage that will not match"); - assert(false); - } catch (error) { - assert(error.message === "No matching version found"); - } - }).timeout(20000); - }); - }); - describe(".getAndCacheSolcByUrl(fileName)", () => { beforeEach(() => { fileName = "someSolcFile"; From fb87890805f6eca7017786f6413bcce4a3c7ca48 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 20:35:32 -0400 Subject: [PATCH 18/35] Fix leaked use of non-listing loading strategies Specify a known version of solc to ensure that neither the Native nor the Local strategy determination codepaths are hit. --- packages/core/lib/commands/compile.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/lib/commands/compile.js b/packages/core/lib/commands/compile.js index 8912bc0fb4c..f9d65e5e55e 100644 --- a/packages/core/lib/commands/compile.js +++ b/packages/core/lib/commands/compile.js @@ -102,6 +102,10 @@ const command = { const supplier = new CompilerSupplier({ solcConfig: { ...options.compilers.solc, + // HACK to force use of the VersionRange or Docker strategy + // as implemented, Docker requires a version to be specified, and so + // we can't simply remove this field entirely. + version: "0.5.16", docker: options.list === "docker" }, events: options.events From 707dc152b62fe88852b830851e974188f0724ee6 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Tue, 3 Aug 2021 17:02:22 -0400 Subject: [PATCH 19/35] Setup TypeScript with allowJs !! breaking change: deep imports must now include `dist/` !! e.g.: ```javascript require("@truffle/compile-solidity/compilerSupplier); ``` becomes: ```javascript require("@truffle/compile-solidity/dist/compilerSupplier); ``` - Move all source files to `src/` - Set up `dist/` as outdir; update references accordingly - Add tsconfig - allowJs - Use `src/` as rootDir as opposed to `./`, which other packages do, since allowJs + declaration causes TS5055 error - Copy the usual typescript-transform-paths pattern + ttypescript - Leave mocha out for now; force `yarn prepare` before running tests --- packages/compile-solidity/.gitignore | 1 + packages/compile-solidity/package.json | 9 ++-- packages/compile-solidity/scripts/test.sh | 2 + .../{ => src}/compileWithPragmaAnalysis.js | 0 .../{ => src}/compilerSupplier/Cache.js | 0 .../{ => src}/compilerSupplier/errors.js | 0 .../{ => src}/compilerSupplier/index.js | 0 .../loadingStrategies/Docker.js | 0 .../loadingStrategies/Local.js | 0 .../loadingStrategies/Native.js | 0 .../loadingStrategies/VersionRange.js | 0 .../loadingStrategies/index.js | 0 .../compilerSupplier/normalizeSolcVersion.js | 0 .../compilerSupplier/observeListeners.js | 0 .../{ => src}/compilerSupplier/rangeUtils.js | 0 packages/compile-solidity/{ => src}/index.js | 0 .../{ => src}/normalizeOptions.js | 0 packages/compile-solidity/{ => src}/parser.js | 0 .../{ => src}/profiler/index.js | 0 .../{ => src}/profiler/loadParser.js | 0 .../{ => src}/profiler/shouldIncludePath.js | 0 .../{ => src}/reportSources.js | 0 packages/compile-solidity/{ => src}/run.js | 0 .../test/compileWithPragmaAnalysis.js | 4 +- .../test/compilerSupplier/index.js | 6 +-- .../loadingStrategies/VersionRange.js | 2 +- .../test/compilerSupplier/rangeUtils.js | 2 +- .../compile-solidity/test/profiler/index.js | 2 +- .../test/profiler/shouldIncludePath.js | 2 +- packages/compile-solidity/test/run.js | 2 +- .../compile-solidity/test/test_metadata.js | 2 +- .../compile-solidity/test/test_ordering.js | 2 +- packages/compile-solidity/test/test_parser.js | 4 +- packages/compile-solidity/tsconfig.json | 41 +++++++++++++++++++ packages/core/lib/testing/SolidityTest.js | 2 +- packages/core/lib/testing/Test.js | 4 +- .../resolver/lib/sources/truffle/Deployed.ts | 2 +- .../resolver/typings/compile-solidity.d.ts | 2 +- yarn.lock | 35 ++++++++++++---- 39 files changed, 96 insertions(+), 30 deletions(-) rename packages/compile-solidity/{ => src}/compileWithPragmaAnalysis.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/Cache.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/errors.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/index.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/loadingStrategies/Docker.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/loadingStrategies/Local.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/loadingStrategies/Native.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/loadingStrategies/VersionRange.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/loadingStrategies/index.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/normalizeSolcVersion.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/observeListeners.js (100%) rename packages/compile-solidity/{ => src}/compilerSupplier/rangeUtils.js (100%) rename packages/compile-solidity/{ => src}/index.js (100%) rename packages/compile-solidity/{ => src}/normalizeOptions.js (100%) rename packages/compile-solidity/{ => src}/parser.js (100%) rename packages/compile-solidity/{ => src}/profiler/index.js (100%) rename packages/compile-solidity/{ => src}/profiler/loadParser.js (100%) rename packages/compile-solidity/{ => src}/profiler/shouldIncludePath.js (100%) rename packages/compile-solidity/{ => src}/reportSources.js (100%) rename packages/compile-solidity/{ => src}/run.js (100%) create mode 100644 packages/compile-solidity/tsconfig.json diff --git a/packages/compile-solidity/.gitignore b/packages/compile-solidity/.gitignore index 22fbdbe4017..acb715d0010 100644 --- a/packages/compile-solidity/.gitignore +++ b/packages/compile-solidity/.gitignore @@ -1,3 +1,4 @@ +dist node_modules yarn.lock package-lock.json diff --git a/packages/compile-solidity/package.json b/packages/compile-solidity/package.json index 872f5113476..1be9fd619ea 100644 --- a/packages/compile-solidity/package.json +++ b/packages/compile-solidity/package.json @@ -13,9 +13,9 @@ "url": "https://github.com/trufflesuite/truffle/issues" }, "version": "5.3.17", - "main": "index.js", + "main": "dist/index.js", "scripts": { - "prepare": "exit 0", + "prepare": "ttsc", "test": "./scripts/test.sh" }, "dependencies": { @@ -47,7 +47,10 @@ "glob": "^7.1.6", "mocha": "8.1.2", "sinon": "^9.0.2", - "tmp": "^0.2.1" + "tmp": "^0.2.1", + "ttypescript": "^1.5.12", + "typescript": "^4.3.5", + "typescript-transform-paths": "^3.1.0" }, "keywords": [ "compile", diff --git a/packages/compile-solidity/scripts/test.sh b/packages/compile-solidity/scripts/test.sh index d744432494c..e6771b3a92b 100755 --- a/packages/compile-solidity/scripts/test.sh +++ b/packages/compile-solidity/scripts/test.sh @@ -2,6 +2,8 @@ set -o errexit +yarn prepare + if [ "$CI" = true ]; then mocha ./test/** ./test/**/* --timeout 70000 $@ else diff --git a/packages/compile-solidity/compileWithPragmaAnalysis.js b/packages/compile-solidity/src/compileWithPragmaAnalysis.js similarity index 100% rename from packages/compile-solidity/compileWithPragmaAnalysis.js rename to packages/compile-solidity/src/compileWithPragmaAnalysis.js diff --git a/packages/compile-solidity/compilerSupplier/Cache.js b/packages/compile-solidity/src/compilerSupplier/Cache.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/Cache.js rename to packages/compile-solidity/src/compilerSupplier/Cache.js diff --git a/packages/compile-solidity/compilerSupplier/errors.js b/packages/compile-solidity/src/compilerSupplier/errors.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/errors.js rename to packages/compile-solidity/src/compilerSupplier/errors.js diff --git a/packages/compile-solidity/compilerSupplier/index.js b/packages/compile-solidity/src/compilerSupplier/index.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/index.js rename to packages/compile-solidity/src/compilerSupplier/index.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/loadingStrategies/Docker.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/loadingStrategies/Local.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/loadingStrategies/Native.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/loadingStrategies/VersionRange.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.js diff --git a/packages/compile-solidity/compilerSupplier/loadingStrategies/index.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/loadingStrategies/index.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js diff --git a/packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js b/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/normalizeSolcVersion.js rename to packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.js diff --git a/packages/compile-solidity/compilerSupplier/observeListeners.js b/packages/compile-solidity/src/compilerSupplier/observeListeners.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/observeListeners.js rename to packages/compile-solidity/src/compilerSupplier/observeListeners.js diff --git a/packages/compile-solidity/compilerSupplier/rangeUtils.js b/packages/compile-solidity/src/compilerSupplier/rangeUtils.js similarity index 100% rename from packages/compile-solidity/compilerSupplier/rangeUtils.js rename to packages/compile-solidity/src/compilerSupplier/rangeUtils.js diff --git a/packages/compile-solidity/index.js b/packages/compile-solidity/src/index.js similarity index 100% rename from packages/compile-solidity/index.js rename to packages/compile-solidity/src/index.js diff --git a/packages/compile-solidity/normalizeOptions.js b/packages/compile-solidity/src/normalizeOptions.js similarity index 100% rename from packages/compile-solidity/normalizeOptions.js rename to packages/compile-solidity/src/normalizeOptions.js diff --git a/packages/compile-solidity/parser.js b/packages/compile-solidity/src/parser.js similarity index 100% rename from packages/compile-solidity/parser.js rename to packages/compile-solidity/src/parser.js diff --git a/packages/compile-solidity/profiler/index.js b/packages/compile-solidity/src/profiler/index.js similarity index 100% rename from packages/compile-solidity/profiler/index.js rename to packages/compile-solidity/src/profiler/index.js diff --git a/packages/compile-solidity/profiler/loadParser.js b/packages/compile-solidity/src/profiler/loadParser.js similarity index 100% rename from packages/compile-solidity/profiler/loadParser.js rename to packages/compile-solidity/src/profiler/loadParser.js diff --git a/packages/compile-solidity/profiler/shouldIncludePath.js b/packages/compile-solidity/src/profiler/shouldIncludePath.js similarity index 100% rename from packages/compile-solidity/profiler/shouldIncludePath.js rename to packages/compile-solidity/src/profiler/shouldIncludePath.js diff --git a/packages/compile-solidity/reportSources.js b/packages/compile-solidity/src/reportSources.js similarity index 100% rename from packages/compile-solidity/reportSources.js rename to packages/compile-solidity/src/reportSources.js diff --git a/packages/compile-solidity/run.js b/packages/compile-solidity/src/run.js similarity index 100% rename from packages/compile-solidity/run.js rename to packages/compile-solidity/src/run.js diff --git a/packages/compile-solidity/test/compileWithPragmaAnalysis.js b/packages/compile-solidity/test/compileWithPragmaAnalysis.js index 9dba380da26..e997583f3f2 100644 --- a/packages/compile-solidity/test/compileWithPragmaAnalysis.js +++ b/packages/compile-solidity/test/compileWithPragmaAnalysis.js @@ -1,9 +1,9 @@ const assert = require("assert"); const Config = require("@truffle/config"); -const {CompilerSupplier} = require("../index"); +const {CompilerSupplier} = require("../dist/index"); const Resolver = require("@truffle/resolver"); const sinon = require("sinon"); -const {compileWithPragmaAnalysis} = require("../compileWithPragmaAnalysis"); +const {compileWithPragmaAnalysis} = require("../dist/compileWithPragmaAnalysis"); const path = require("path"); let paths = []; diff --git a/packages/compile-solidity/test/compilerSupplier/index.js b/packages/compile-solidity/test/compilerSupplier/index.js index 0efbcba647d..d4f94a3760f 100644 --- a/packages/compile-solidity/test/compilerSupplier/index.js +++ b/packages/compile-solidity/test/compilerSupplier/index.js @@ -1,14 +1,14 @@ const assert = require("assert"); const sinon = require("sinon"); const axios = require("axios"); -const CompilerSupplier = require("../../compilerSupplier"); -const Cache = require("../../compilerSupplier/Cache"); +const CompilerSupplier = require("../../dist/compilerSupplier"); +const Cache = require("../../dist/compilerSupplier/Cache"); const { Docker, Native, Local, VersionRange -} = require("../../compilerSupplier/loadingStrategies"); +} = require("../../dist/compilerSupplier/loadingStrategies"); const Config = require("@truffle/config"); const config = new Config(); let supplier; diff --git a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js index bf5e6ca64a9..40ee6ae0eb3 100644 --- a/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/test/compilerSupplier/loadingStrategies/VersionRange.js @@ -2,7 +2,7 @@ const assert = require("assert"); const fs = require("fs"); const axios = require("axios"); const sinon = require("sinon"); -const { VersionRange } = require("../../../compilerSupplier/loadingStrategies"); +const { VersionRange } = require("../../../dist/compilerSupplier/loadingStrategies"); const Config = require("@truffle/config"); const config = Config.default(); let versionRangeOptions = { diff --git a/packages/compile-solidity/test/compilerSupplier/rangeUtils.js b/packages/compile-solidity/test/compilerSupplier/rangeUtils.js index 0d872c143ff..9d85efb0389 100644 --- a/packages/compile-solidity/test/compilerSupplier/rangeUtils.js +++ b/packages/compile-solidity/test/compilerSupplier/rangeUtils.js @@ -1,5 +1,5 @@ const assert = require("assert"); -const { rangeContainsAtLeast } = require("../../compilerSupplier/rangeUtils"); +const { rangeContainsAtLeast } = require("../../dist/compilerSupplier/rangeUtils"); describe("rangeUtils", () => { describe(".rangeContainsAtLeast(range, comparisonVersion)", () => { diff --git a/packages/compile-solidity/test/profiler/index.js b/packages/compile-solidity/test/profiler/index.js index 39cb1d2d1a3..98317f8d5b6 100644 --- a/packages/compile-solidity/test/profiler/index.js +++ b/packages/compile-solidity/test/profiler/index.js @@ -2,7 +2,7 @@ var assert = require("chai").assert; var fs = require("fs-extra"); var glob = require("glob"); var { default: Box } = require("@truffle/box"); -var Profiler = require("../../profiler"); +var Profiler = require("../../dist/profiler"); var Resolver = require("@truffle/resolver"); var Artifactor = require("@truffle/artifactor"); diff --git a/packages/compile-solidity/test/profiler/shouldIncludePath.js b/packages/compile-solidity/test/profiler/shouldIncludePath.js index ddd8ee6a6a5..f59beddb401 100644 --- a/packages/compile-solidity/test/profiler/shouldIncludePath.js +++ b/packages/compile-solidity/test/profiler/shouldIncludePath.js @@ -1,5 +1,5 @@ const assert = require("assert"); -const { shouldIncludePath } = require("../../profiler/shouldIncludePath"); +const { shouldIncludePath } = require("../../dist/profiler/shouldIncludePath"); describe(".shouldIncludePath(filename)", () => { it("returns true if the file has a .sol extension", () => { diff --git a/packages/compile-solidity/test/run.js b/packages/compile-solidity/test/run.js index 1526c653e55..711b2dbd6da 100644 --- a/packages/compile-solidity/test/run.js +++ b/packages/compile-solidity/test/run.js @@ -1,6 +1,6 @@ const Config = require("@truffle/config"); const { assert } = require("chai"); -const { run } = require("../run"); +const { run } = require("../dist/run"); let rawSources, options; describe("async run(rawSources, options)", () => { diff --git a/packages/compile-solidity/test/test_metadata.js b/packages/compile-solidity/test/test_metadata.js index cd998613930..51d07e52027 100644 --- a/packages/compile-solidity/test/test_metadata.js +++ b/packages/compile-solidity/test/test_metadata.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile:test:test_metadata"); const fs = require("fs"); const path = require("path"); const { Compile } = require("@truffle/compile-solidity"); -const CompilerSupplier = require("../compilerSupplier"); +const CompilerSupplier = require("../dist/compilerSupplier"); const assert = require("assert"); const { findOne } = require("./helpers"); const workingDirectory = "/home/fakename/truffleproject"; diff --git a/packages/compile-solidity/test/test_ordering.js b/packages/compile-solidity/test/test_ordering.js index 128020ad4a2..80152d77aee 100644 --- a/packages/compile-solidity/test/test_ordering.js +++ b/packages/compile-solidity/test/test_ordering.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile:test:test_ordering"); const fs = require("fs"); const path = require("path"); const { Compile } = require("@truffle/compile-solidity"); -const CompilerSupplier = require("../compilerSupplier"); +const CompilerSupplier = require("../dist/compilerSupplier"); const assert = require("assert"); const { findOne } = require("./helpers"); let compileOptions = { diff --git a/packages/compile-solidity/test/test_parser.js b/packages/compile-solidity/test/test_parser.js index 1796abb4e86..8d6b759cc99 100644 --- a/packages/compile-solidity/test/test_parser.js +++ b/packages/compile-solidity/test/test_parser.js @@ -1,7 +1,7 @@ const fs = require("fs"); const path = require("path"); -const Parser = require("../parser"); -const CompilerSupplier = require("../compilerSupplier"); +const Parser = require("../dist/parser"); +const CompilerSupplier = require("../dist/compilerSupplier"); const assert = require("assert"); describe("Parser", () => { diff --git a/packages/compile-solidity/tsconfig.json b/packages/compile-solidity/tsconfig.json new file mode 100644 index 00000000000..7c7c4d7fbeb --- /dev/null +++ b/packages/compile-solidity/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "sourceMap": true, + "declaration": true, + "allowJs": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "skipLibCheck": true, + "target": "es6", + "moduleResolution": "node", + "downlevelIteration": true, + "allowSyntheticDefaultImports": true, + "module": "commonjs", + "outDir": "./dist", + "strictBindCallApply": true, + "strictNullChecks": true, + "paths": { + "@truffle/compile-solidity": ["./src"], + "@truffle/compile-solidity/*": ["./src/*"], + "test/*": ["./test/*"] + }, + "rootDir": "src", + "baseUrl": ".", + "typeRoots": [], + "types": [ + "mocha", + "node" + ], + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typescript-transform-paths", "afterDeclarations": true } + ] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/packages/core/lib/testing/SolidityTest.js b/packages/core/lib/testing/SolidityTest.js index 4b1e7dd600f..df689f229ef 100644 --- a/packages/core/lib/testing/SolidityTest.js +++ b/packages/core/lib/testing/SolidityTest.js @@ -3,7 +3,7 @@ const Suite = require("mocha/lib/suite.js"); const Deployer = require("@truffle/deployer"); const { Compile } = require("@truffle/compile-solidity"); const { Shims } = require("@truffle/compile-common"); -const RangeUtils = require("@truffle/compile-solidity/compilerSupplier/rangeUtils"); +const RangeUtils = require("@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"); const debug = require("debug")("lib:testing:soliditytest"); const SolidityTest = { diff --git a/packages/core/lib/testing/Test.js b/packages/core/lib/testing/Test.js index 4cd00b767ba..cb1c90ca3d8 100644 --- a/packages/core/lib/testing/Test.js +++ b/packages/core/lib/testing/Test.js @@ -10,10 +10,10 @@ const WorkflowCompile = require("@truffle/workflow-compile"); const Resolver = require("@truffle/resolver"); const TestRunner = require("./TestRunner"); const SolidityTest = require("./SolidityTest"); -const RangeUtils = require("@truffle/compile-solidity/compilerSupplier/rangeUtils"); +const RangeUtils = require("@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"); const expect = require("@truffle/expect"); const Migrate = require("@truffle/migrate"); -const Profiler = require("@truffle/compile-solidity/profiler"); +const Profiler = require("@truffle/compile-solidity/dist/profiler"); const originalrequire = require("original-require"); const Codec = require("@truffle/codec"); const debug = require("debug")("lib:test"); diff --git a/packages/resolver/lib/sources/truffle/Deployed.ts b/packages/resolver/lib/sources/truffle/Deployed.ts index 9fb86c387cc..6b87c3acb52 100644 --- a/packages/resolver/lib/sources/truffle/Deployed.ts +++ b/packages/resolver/lib/sources/truffle/Deployed.ts @@ -1,6 +1,6 @@ const web3Utils = require("web3-utils"); import semver from "semver"; -import RangeUtils from "@truffle/compile-solidity/compilerSupplier/rangeUtils"; +import RangeUtils from "@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"; type solcOptionsArg = { solc: { version: string }; diff --git a/packages/resolver/typings/compile-solidity.d.ts b/packages/resolver/typings/compile-solidity.d.ts index fa4c1cadc4a..f65f4774e57 100644 --- a/packages/resolver/typings/compile-solidity.d.ts +++ b/packages/resolver/typings/compile-solidity.d.ts @@ -1 +1 @@ -declare module "@truffle/compile-solidity/compilerSupplier/rangeUtils"; +declare module "@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"; diff --git a/yarn.lock b/yarn.lock index 17b08cc853f..f7b6fddc0a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24666,6 +24666,14 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@>=1.9.0, resolve@^1.17.0, resolve@^1.4.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + resolve@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" @@ -24688,14 +24696,6 @@ resolve@^1.11.1: is-core-module "^2.0.0" path-parse "^1.0.6" -resolve@^1.17.0, resolve@^1.4.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - resolve@^1.18.1: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" @@ -27533,6 +27533,13 @@ tty-browserify@0.0.1: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== +ttypescript@^1.5.12: + version "1.5.12" + resolved "https://registry.yarnpkg.com/ttypescript/-/ttypescript-1.5.12.tgz#27a8356d7d4e719d0075a8feb4df14b52384f044" + integrity sha512-1ojRyJvpnmgN9kIHmUnQPlEV1gq+VVsxVYjk/NfvMlHSmYxjK5hEvOOU2MQASrbekTUiUM7pR/nXeCc8bzvMOQ== + dependencies: + resolve ">=1.9.0" + ttypescript@^1.5.7: version "1.5.8" resolved "https://registry.yarnpkg.com/ttypescript/-/ttypescript-1.5.8.tgz#82fc316a47be3136e993abf0dbd2768cc68d1db3" @@ -27767,6 +27774,13 @@ typescript-transform-paths@^2.1.0: resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-2.1.0.tgz#3d5c492910c59ce1a7bb762d3078185cbd11940d" integrity sha512-xBzFzS5LTfTjyuT4fuqIk3MLDWsrJTNFs3y17CwVR54G6G/KtTZcVc9UjChgDTuoBffd+Q3oD99UtyDXRpuy/g== +typescript-transform-paths@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-3.1.0.tgz#48f3e50b6b1fb8275cb5657f0651346467da32f4" + integrity sha512-JEdKeRgsTlfuzQABkLLyqMvNsHsbXkTgeOwuVTCVG7RYxpVs6wZSsnf9z39l09LBljOiXXpEv3AL2eQnKlJupA== + dependencies: + minimatch "^3.0.4" + typescript-tuple@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" @@ -27784,6 +27798,11 @@ typescript@^4.1.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.4.tgz#f058636e2f4f83f94ddaae07b20fd5e14598432f" integrity sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg== +typescript@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + typewise-core@^1.2, typewise-core@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" From 2fdcfff24ae097ce44680b9c2fb08d20a4a3b977 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Tue, 3 Aug 2021 17:53:28 -0400 Subject: [PATCH 20/35] Convert compilerSupplier to TS !! breakage: no more default exports anywhere in this module !! --- .../src/compileWithPragmaAnalysis.js | 2 +- .../compilerSupplier/{Cache.js => Cache.ts} | 12 ++--- .../compilerSupplier/{errors.js => errors.ts} | 9 +--- .../compilerSupplier/{index.js => index.ts} | 32 +++++++---- .../{Docker.js => Docker.ts} | 46 +++++++++------- .../loadingStrategies/{Local.js => Local.ts} | 14 +++-- .../{Native.js => Native.ts} | 6 +-- .../{VersionRange.js => VersionRange.ts} | 42 ++++++++------- .../loadingStrategies/index.js | 6 --- .../loadingStrategies/index.ts | 4 ++ ...SolcVersion.js => normalizeSolcVersion.ts} | 4 +- ...bserveListeners.js => observeListeners.ts} | 11 ++-- .../src/compilerSupplier/rangeUtils.js | 54 ------------------- .../src/compilerSupplier/rangeUtils.ts | 45 ++++++++++++++++ packages/compile-solidity/src/index.js | 2 +- .../src/profiler/loadParser.js | 2 +- packages/compile-solidity/src/run.js | 2 +- .../test/compilerSupplier/index.js | 4 +- .../compile-solidity/test/test_metadata.js | 2 +- .../compile-solidity/test/test_ordering.js | 2 +- packages/compile-solidity/test/test_parser.js | 2 +- packages/compile-solidity/tsconfig.json | 5 +- .../resolver/lib/sources/truffle/Deployed.ts | 2 +- 23 files changed, 158 insertions(+), 152 deletions(-) rename packages/compile-solidity/src/compilerSupplier/{Cache.js => Cache.ts} (85%) rename packages/compile-solidity/src/compilerSupplier/{errors.js => errors.ts} (79%) rename packages/compile-solidity/src/compilerSupplier/{index.js => index.ts} (83%) rename packages/compile-solidity/src/compilerSupplier/loadingStrategies/{Docker.js => Docker.ts} (87%) rename packages/compile-solidity/src/compilerSupplier/loadingStrategies/{Local.js => Local.ts} (72%) rename packages/compile-solidity/src/compilerSupplier/loadingStrategies/{Native.js => Native.ts} (92%) rename packages/compile-solidity/src/compilerSupplier/loadingStrategies/{VersionRange.js => VersionRange.ts} (90%) delete mode 100644 packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js create mode 100644 packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.ts rename packages/compile-solidity/src/compilerSupplier/{normalizeSolcVersion.js => normalizeSolcVersion.ts} (71%) rename packages/compile-solidity/src/compilerSupplier/{observeListeners.js => observeListeners.ts} (72%) delete mode 100644 packages/compile-solidity/src/compilerSupplier/rangeUtils.js create mode 100644 packages/compile-solidity/src/compilerSupplier/rangeUtils.ts diff --git a/packages/compile-solidity/src/compileWithPragmaAnalysis.js b/packages/compile-solidity/src/compileWithPragmaAnalysis.js index 5bef16c4bfe..66cae05e172 100644 --- a/packages/compile-solidity/src/compileWithPragmaAnalysis.js +++ b/packages/compile-solidity/src/compileWithPragmaAnalysis.js @@ -1,4 +1,4 @@ -const CompilerSupplier = require("./compilerSupplier"); +const { CompilerSupplier } = require("./compilerSupplier"); const Config = require("@truffle/config"); const semver = require("semver"); const Profiler = require("./profiler"); diff --git a/packages/compile-solidity/src/compilerSupplier/Cache.js b/packages/compile-solidity/src/compilerSupplier/Cache.ts similarity index 85% rename from packages/compile-solidity/src/compilerSupplier/Cache.js rename to packages/compile-solidity/src/compilerSupplier/Cache.ts index e5659c58d9f..12fb6f9c401 100644 --- a/packages/compile-solidity/src/compilerSupplier/Cache.js +++ b/packages/compile-solidity/src/compilerSupplier/Cache.ts @@ -1,8 +1,10 @@ -const Config = require("@truffle/config"); -const path = require("path"); -const fs = require("fs"); +import Config from "@truffle/config"; +import path from "path"; +import fs from "fs"; + +export class Cache { + private compilerCachePath: string; -class Cache { constructor() { const compilersDir = path.resolve( Config.getTruffleDataDirectory(), @@ -33,5 +35,3 @@ class Cache { return path.resolve(this.compilerCachePath, fileName); } }; - -module.exports = Cache; diff --git a/packages/compile-solidity/src/compilerSupplier/errors.js b/packages/compile-solidity/src/compilerSupplier/errors.ts similarity index 79% rename from packages/compile-solidity/src/compilerSupplier/errors.js rename to packages/compile-solidity/src/compilerSupplier/errors.ts index b0ae00ecda3..22683f8a090 100644 --- a/packages/compile-solidity/src/compilerSupplier/errors.js +++ b/packages/compile-solidity/src/compilerSupplier/errors.ts @@ -1,4 +1,4 @@ -class NoVersionError extends Error { +export class NoVersionError extends Error { constructor(input) { const message = `Could not find a compiler version matching ${input}. ` + `Please ensure you are specifying a valid version, constraint or ` + @@ -8,7 +8,7 @@ class NoVersionError extends Error { } } -class NoRequestError extends Error { +export class NoRequestError extends Error { constructor(input, error) { const message = `Failed to complete request to: ${input}. Are you connected to ` + @@ -17,8 +17,3 @@ class NoRequestError extends Error { super(message); } } - -module.exports = { - NoVersionError, - NoRequestError -}; diff --git a/packages/compile-solidity/src/compilerSupplier/index.js b/packages/compile-solidity/src/compilerSupplier/index.ts similarity index 83% rename from packages/compile-solidity/src/compilerSupplier/index.js rename to packages/compile-solidity/src/compilerSupplier/index.ts index 6ab0392c4d1..4d82714087c 100644 --- a/packages/compile-solidity/src/compilerSupplier/index.js +++ b/packages/compile-solidity/src/compilerSupplier/index.ts @@ -1,12 +1,24 @@ -const path = require("path"); -const fs = require("fs"); -const semver = require("semver"); +import path from "path"; +import fs from "fs"; +import semver from "semver"; -const { Docker, Local, Native, VersionRange } = require("./loadingStrategies"); +import { Docker, Local, Native, VersionRange } from "./loadingStrategies"; const defaultSolcVersion = "0.5.16"; -class CompilerSupplier { +export class CompilerSupplier { + private events: unknown; + private version: string; + private docker: unknown; + private compilerRoots: unknown; + private strategyOptions: Partial<{ + version: string; + docker: unknown; + compilerRoots: unknown; + events: unknown; + spawn: unknown; + }>; + constructor({ events, solcConfig }) { const { version, docker, compilerRoots, spawn } = solcConfig; this.events = events; @@ -34,9 +46,9 @@ class CompilerSupplier { if (useDocker) { strategy = new Docker(this.strategyOptions); } else if (useNative) { - strategy = new Native(this.strategyOptions); + strategy = new Native(); } else if (useSpecifiedLocal) { - strategy = new Local(this.strategyOptions); + strategy = new Local(); } else if (isValidVersionRange) { strategy = new VersionRange(this.strategyOptions); } @@ -62,9 +74,9 @@ class CompilerSupplier { if (useDocker) { strategy = new Docker(this.strategyOptions); } else if (useNative) { - strategy = new Native(this.strategyOptions); + strategy = new Native(); } else if (useSpecifiedLocal) { - strategy = new Local(this.strategyOptions); + strategy = new Local(); } else if (isValidVersionRange) { strategy = new VersionRange(this.strategyOptions); } @@ -101,5 +113,3 @@ class CompilerSupplier { return fs.existsSync(localPath) || path.isAbsolute(localPath); } } - -module.exports = CompilerSupplier; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts similarity index 87% rename from packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts index 414ac2b67da..a5132f1ab93 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.js +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts @@ -1,15 +1,24 @@ -const axios = require("axios"); -const axiosRetry = require("axios-retry"); -const fs = require("fs"); -const { execSync } = require("child_process"); -const ora = require("ora"); -const semver = require("semver"); -const Cache = require("../Cache"); -const { normalizeSolcVersion } = require("../normalizeSolcVersion"); -const { NoVersionError, NoRequestError } = require("../errors"); -const { asyncFirst, asyncFilter, asyncFork } = require("iter-tools"); - -class Docker { +import axios from "axios"; +import axiosRetry from "axios-retry"; +import fs from "fs"; +import { execSync } from "child_process"; +import ora from "ora"; +import semver from "semver"; +import { Cache } from "../Cache"; +import { normalizeSolcVersion } from "../normalizeSolcVersion"; +import { NoVersionError, NoRequestError } from "../errors"; +import { asyncFirst, asyncFilter, asyncFork } from "iter-tools"; + +export class Docker { + private config: { + spawn: { + maxBuffer: number; + }; + dockerTagsUrl: string; + version: string; + }; + private cache: Cache; + constructor(options) { const defaultConfig = { dockerTagsUrl: @@ -75,8 +84,9 @@ class Docker { // grab the latest release from the forked releases stream; // coerce semver to remove possible `-alpine` suffix used by this repo - const latestRelease = semver.coerce(await asyncFirst(forkedReleases)) - .version; + const latestRelease = semver.coerce( + await asyncFirst(forkedReleases) + )?.version; return { prereleases, @@ -157,7 +167,7 @@ class Docker { const tooManyRequests = error && error.response && error.response.status === 429; - return ( + return !!( axiosRetry.isNetworkOrIdempotentRequestError(error) || tooManyRequests ); } @@ -191,7 +201,7 @@ class Docker { } } -class NoDockerError extends Error { +export class NoDockerError extends Error { constructor() { super( "You are trying to run dockerized solc, but docker is not installed." @@ -199,7 +209,7 @@ class NoDockerError extends Error { } } -class NoStringError extends Error { +export class NoStringError extends Error { constructor(input) { const message = "`compilers.solc.version` option must be a string specifying:\n" + @@ -213,5 +223,3 @@ class NoStringError extends Error { super(message); } } - -module.exports = Docker; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.ts similarity index 72% rename from packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.ts index 841a91cb256..93fafe85dc4 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.js +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Local.ts @@ -1,9 +1,9 @@ -const path = require("path"); -const originalRequire = require("original-require"); -const solcWrap = require("solc/wrapper"); -const observeListeners = require("../observeListeners"); +import path from "path"; +import originalRequire from "original-require"; +import solcWrap from "solc/wrapper"; +import { observeListeners } from "../observeListeners"; -class Local { +export class Local { load(localPath) { const listeners = observeListeners(); try { @@ -25,11 +25,9 @@ class Local { } } -class NoPathError extends Error { +export class NoPathError extends Error { constructor(input, error) { const message = `Could not find compiler at: ${input}\n\n` + error; super(message); } } - -module.exports = Local; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.ts similarity index 92% rename from packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.ts index f9f5fdfc78d..71a3aa4c581 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.js +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Native.ts @@ -2,7 +2,7 @@ const { execSync } = require("child_process"); const { normalizeSolcVersion } = require("../normalizeSolcVersion"); const { NoVersionError } = require("../errors"); -class Native { +export class Native { load() { const versionString = this.validateAndGetSolcVersion(); const command = "solc --standard-json"; @@ -33,10 +33,8 @@ class Native { } } -class NoNativeError extends Error { +export class NoNativeError extends Error { constructor(error) { super("Could not execute local solc binary: " + error); } } - -module.exports = Native; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts similarity index 90% rename from packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.js rename to packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts index 750ca643525..2e51a98da3e 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.js +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts @@ -1,14 +1,23 @@ -const debug = require("debug")("compile:compilerSupplier"); -const requireFromString = require("require-from-string"); -const originalRequire = require("original-require"); -const axios = require("axios").default; -const semver = require("semver"); -const solcWrap = require("solc/wrapper"); -const Cache = require("../Cache"); -const observeListeners = require("../observeListeners"); -const { NoVersionError, NoRequestError } = require("../errors"); - -class VersionRange { +import debugModule from "debug"; +const debug = debugModule("compile:compilerSupplier"); + +import requireFromString from "require-from-string"; +import originalRequire from "original-require"; +import axios from "axios"; +import semver from "semver"; +import solcWrap from "solc/wrapper"; +import { Cache } from "../Cache"; +import { observeListeners } from "../observeListeners"; +import { NoVersionError, NoRequestError } from "../errors"; + +export class VersionRange { + private config: { + events: any; + compilerRoots: string[]; + }; + + private cache: Cache; + constructor(options) { const defaultConfig = { compilerRoots: [ @@ -27,7 +36,7 @@ class VersionRange { this.cache = new Cache(); } - async load(versionRange) { + async load(versionRange: string) { const rangeIsSingleVersion = semver.valid(versionRange); if (rangeIsSingleVersion && this.versionIsCached(versionRange)) { return this.getCachedSolcByVersionRange(versionRange); @@ -77,7 +86,7 @@ class VersionRange { .map(solcVersion => { if (semver.satisfies(solcVersion, version)) return solcVersion; }) - .filter(solcVersion => solcVersion); + .filter((solcVersion): solcVersion is string => !!solcVersion); if (satisfyingVersions.length > 0) { return satisfyingVersions.reduce((newestVersion, version) => { return semver.gtr(version, newestVersion) ? version : newestVersion; @@ -239,18 +248,15 @@ class VersionRange { const cachedVersions = cachedCompilerFileNames.map(fileName => { const match = fileName.match(/v\d+\.\d+\.\d+.*/); if (match) return match[0]; - }); + }).filter((version): version is string => !!version); return cachedVersions.find(cachedVersion => semver.satisfies(cachedVersion, version) ); } } -class NoUrlError extends Error { +export class NoUrlError extends Error { constructor() { super("compiler root URL missing"); } } - - -module.exports = VersionRange; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js deleted file mode 100644 index 49c08d3c76a..00000000000 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - Docker: require("./Docker"), - Local: require("./Local"), - Native: require("./Native"), - VersionRange: require("./VersionRange") -}; diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.ts b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.ts new file mode 100644 index 00000000000..67d9bb45035 --- /dev/null +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/index.ts @@ -0,0 +1,4 @@ +export { Docker } from "./Docker"; +export { Local } from "./Local"; +export { Native } from "./Native"; +export { VersionRange } from "./VersionRange"; diff --git a/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.js b/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts similarity index 71% rename from packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.js rename to packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts index 45699a75684..d043740954a 100644 --- a/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.js +++ b/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts @@ -2,9 +2,7 @@ * Convert a full version string (possibly with commit information, etc.) into * a canonical short-form semver version (x.y.z) */ -const normalizeSolcVersion = (input) => { +export const normalizeSolcVersion = (input) => { const version = String(input); return version.split(":")[1].trim(); }; - -module.exports = { normalizeSolcVersion }; diff --git a/packages/compile-solidity/src/compilerSupplier/observeListeners.js b/packages/compile-solidity/src/compilerSupplier/observeListeners.ts similarity index 72% rename from packages/compile-solidity/src/compilerSupplier/observeListeners.js rename to packages/compile-solidity/src/compilerSupplier/observeListeners.ts index d4880d05e0f..5213a37c75c 100644 --- a/packages/compile-solidity/src/compilerSupplier/observeListeners.js +++ b/packages/compile-solidity/src/compilerSupplier/observeListeners.ts @@ -1,10 +1,16 @@ -const observeListeners = () => { +export const observeListeners = () => { const listeners = new ObservedListeners(); return listeners; }; + class ObservedListeners { + private listeners: { + uncaughtException: Set; + unhandledRejection: Set; + }; + constructor() { this.listeners = { uncaughtException: new Set(process.listeners("uncaughtException")), @@ -18,6 +24,7 @@ class ObservedListeners { cleanup() { for (const eventName in this.listeners) { const marked = this.listeners[eventName]; + // @ts-expect-error since eventName: string for (const listener of process.listeners(eventName)) { if (!marked.has(listener)) { process.removeListener(eventName, listener); @@ -26,5 +33,3 @@ class ObservedListeners { } } } - -module.exports = observeListeners; diff --git a/packages/compile-solidity/src/compilerSupplier/rangeUtils.js b/packages/compile-solidity/src/compilerSupplier/rangeUtils.js deleted file mode 100644 index b6ba0bd510a..00000000000 --- a/packages/compile-solidity/src/compilerSupplier/rangeUtils.js +++ /dev/null @@ -1,54 +0,0 @@ -const semver = require("semver"); -const CompilerSupplier = require("."); -const Native = require("./loadingStrategies/Native"); -const Local = require("./loadingStrategies/Local"); -const path = require("path"); -const fs = require("fs-extra"); - -const RangeUtils = { - //takes a version string which may be native or local, and resolves - //it to one which is (presumably) either a version or a version range - resolveToRange: function (version) { - if (!version) { - return CompilerSupplier.getDefaultVersion(); - } - //if version was native or local, must determine what version that - //actually corresponds to - if (version === "native") { - return new Native().load().version(); - } else if (fs.existsSync(version) && path.isAbsolute(version)) { - return new Local({ version }).load().version(); - } - return version; - }, - - //parameter range may be either an individual version or a range - rangeContainsAtLeast: function (range, comparisonVersion) { - //the following line works with prereleases - const individualAtLeast = - semver.valid(range, { loose: true }) && - semver.gte(range, comparisonVersion, { - includePrerelease: true, - loose: true - }); - //the following line doesn't, despite the flag, but does work with version ranges - const rangeAtLeast = - semver.validRange(range, { loose: true }) && - !semver.gtr(comparisonVersion, range, { - includePrerelease: true, - loose: true - }); //intersects will throw if given undefined so must ward against - return individualAtLeast || rangeAtLeast; - }, - - //parameter version may be either an individual version or a range - //first case is there to handle ranges, second to handle anything else - coerce: function (version) { - return ( - semver.validRange(version, { loose: true }) || - semver.coerce(version, { loose: true }).toString() - ); - } -}; - -module.exports = RangeUtils; diff --git a/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts b/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts new file mode 100644 index 00000000000..4e0914f5fb0 --- /dev/null +++ b/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts @@ -0,0 +1,45 @@ +import path from "path"; +import fs from "fs-extra"; +import semver from "semver"; +import { Native, Local } from "./loadingStrategies"; +import { CompilerSupplier } from "./index"; + +/** + * takes a version string which may be native or local, and resolves + * it to one which is (presumably) either a version or a version range + */ +export function resolveToRange(version) { + if (!version) { + return CompilerSupplier.getDefaultVersion(); + } + + //if version was native or local, must determine what version that + //actually corresponds to + if (version === "native") { + return new Native().load().version(); + } else if (fs.existsSync(version) && path.isAbsolute(version)) { + return new Local().load(version).version(); + } + return version; +} + +/** + * parameter range may be either an individual version or a range + */ +export function rangeContainsAtLeast(range, comparisonVersion) { + //the following line works with prereleases + const individualAtLeast = + semver.valid(range, { loose: true }) && + semver.gte(range, comparisonVersion, { + includePrerelease: true, + loose: true + }); + //the following line doesn't, despite the flag, but does work with version ranges + const rangeAtLeast = + semver.validRange(range, { loose: true }) && + !semver.gtr(comparisonVersion, range, { + includePrerelease: true, + loose: true + }); //intersects will throw if given undefined so must ward against + return individualAtLeast || rangeAtLeast; +} diff --git a/packages/compile-solidity/src/index.js b/packages/compile-solidity/src/index.js index 3f3d090de67..c78fe0f33ae 100644 --- a/packages/compile-solidity/src/index.js +++ b/packages/compile-solidity/src/index.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile"); const findContracts = require("@truffle/contract-sources"); const Config = require("@truffle/config"); const Profiler = require("./profiler"); -const CompilerSupplier = require("./compilerSupplier"); +const { CompilerSupplier } = require("./compilerSupplier"); const { run } = require("./run"); const { normalizeOptions } = require("./normalizeOptions"); const { compileWithPragmaAnalysis } = require("./compileWithPragmaAnalysis"); diff --git a/packages/compile-solidity/src/profiler/loadParser.js b/packages/compile-solidity/src/profiler/loadParser.js index bf9774c314a..e1675821a7f 100644 --- a/packages/compile-solidity/src/profiler/loadParser.js +++ b/packages/compile-solidity/src/profiler/loadParser.js @@ -1,4 +1,4 @@ -const CompilerSupplier = require("../compilerSupplier"); +const { CompilerSupplier } = require("../compilerSupplier"); const Parser = require("../parser"); const semver = require("semver"); diff --git a/packages/compile-solidity/src/run.js b/packages/compile-solidity/src/run.js index b841e9e4518..23cbb894804 100644 --- a/packages/compile-solidity/src/run.js +++ b/packages/compile-solidity/src/run.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile:run"); const OS = require("os"); const semver = require("semver"); const Common = require("@truffle/compile-common"); -const CompilerSupplier = require("./compilerSupplier"); +const { CompilerSupplier } = require("./compilerSupplier"); // this function returns a Compilation - legacy/index.js and ./index.js // both check to make sure rawSources exist before calling this method diff --git a/packages/compile-solidity/test/compilerSupplier/index.js b/packages/compile-solidity/test/compilerSupplier/index.js index d4f94a3760f..78b5ecf8953 100644 --- a/packages/compile-solidity/test/compilerSupplier/index.js +++ b/packages/compile-solidity/test/compilerSupplier/index.js @@ -1,8 +1,8 @@ const assert = require("assert"); const sinon = require("sinon"); const axios = require("axios"); -const CompilerSupplier = require("../../dist/compilerSupplier"); -const Cache = require("../../dist/compilerSupplier/Cache"); +const { CompilerSupplier } = require("../../dist/compilerSupplier"); +const { Cache } = require("../../dist/compilerSupplier/Cache"); const { Docker, Native, diff --git a/packages/compile-solidity/test/test_metadata.js b/packages/compile-solidity/test/test_metadata.js index 51d07e52027..a2fd9cb4166 100644 --- a/packages/compile-solidity/test/test_metadata.js +++ b/packages/compile-solidity/test/test_metadata.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile:test:test_metadata"); const fs = require("fs"); const path = require("path"); const { Compile } = require("@truffle/compile-solidity"); -const CompilerSupplier = require("../dist/compilerSupplier"); +const { CompilerSupplier } = require("../dist/compilerSupplier"); const assert = require("assert"); const { findOne } = require("./helpers"); const workingDirectory = "/home/fakename/truffleproject"; diff --git a/packages/compile-solidity/test/test_ordering.js b/packages/compile-solidity/test/test_ordering.js index 80152d77aee..fe49d789a08 100644 --- a/packages/compile-solidity/test/test_ordering.js +++ b/packages/compile-solidity/test/test_ordering.js @@ -2,7 +2,7 @@ const debug = require("debug")("compile:test:test_ordering"); const fs = require("fs"); const path = require("path"); const { Compile } = require("@truffle/compile-solidity"); -const CompilerSupplier = require("../dist/compilerSupplier"); +const { CompilerSupplier } = require("../dist/compilerSupplier"); const assert = require("assert"); const { findOne } = require("./helpers"); let compileOptions = { diff --git a/packages/compile-solidity/test/test_parser.js b/packages/compile-solidity/test/test_parser.js index 8d6b759cc99..523b7275ba3 100644 --- a/packages/compile-solidity/test/test_parser.js +++ b/packages/compile-solidity/test/test_parser.js @@ -1,7 +1,7 @@ const fs = require("fs"); const path = require("path"); const Parser = require("../dist/parser"); -const CompilerSupplier = require("../dist/compilerSupplier"); +const { CompilerSupplier } = require("../dist/compilerSupplier"); const assert = require("assert"); describe("Parser", () => { diff --git a/packages/compile-solidity/tsconfig.json b/packages/compile-solidity/tsconfig.json index 7c7c4d7fbeb..d530800f011 100644 --- a/packages/compile-solidity/tsconfig.json +++ b/packages/compile-solidity/tsconfig.json @@ -21,10 +21,9 @@ }, "rootDir": "src", "baseUrl": ".", - "typeRoots": [], + "typeRoots": ["../../node_modules/@types/node"], "types": [ - "mocha", - "node" + "mocha" ], "plugins": [ { "transform": "typescript-transform-paths" }, diff --git a/packages/resolver/lib/sources/truffle/Deployed.ts b/packages/resolver/lib/sources/truffle/Deployed.ts index 6b87c3acb52..b8041c5238e 100644 --- a/packages/resolver/lib/sources/truffle/Deployed.ts +++ b/packages/resolver/lib/sources/truffle/Deployed.ts @@ -1,6 +1,6 @@ const web3Utils = require("web3-utils"); import semver from "semver"; -import RangeUtils from "@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"; +import * as RangeUtils from "@truffle/compile-solidity/dist/compilerSupplier/rangeUtils"; type solcOptionsArg = { solc: { version: string }; From 9c8eefe19841203a0937b9e5892aa20557b1d2da Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 21:25:59 -0400 Subject: [PATCH 21/35] Type CompilerSupplier instance variables better --- .../compile-solidity/src/compilerSupplier/index.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/compile-solidity/src/compilerSupplier/index.ts b/packages/compile-solidity/src/compilerSupplier/index.ts index 4d82714087c..09445413bc0 100644 --- a/packages/compile-solidity/src/compilerSupplier/index.ts +++ b/packages/compile-solidity/src/compilerSupplier/index.ts @@ -7,16 +7,18 @@ import { Docker, Local, Native, VersionRange } from "./loadingStrategies"; const defaultSolcVersion = "0.5.16"; export class CompilerSupplier { - private events: unknown; + private events: any; private version: string; private docker: unknown; private compilerRoots: unknown; private strategyOptions: Partial<{ version: string; - docker: unknown; - compilerRoots: unknown; - events: unknown; - spawn: unknown; + docker: boolean; + compilerRoots: string[]; + events: any; // represents a @truffle/events instance, which lacks types + spawn: { + maxBuffer: number + }; }>; constructor({ events, solcConfig }) { From fb9bacb8ba06014bd4430e3ec5c20243a5efb186 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 21:28:30 -0400 Subject: [PATCH 22/35] Fix weird boolean casting --- .../src/compilerSupplier/loadingStrategies/Docker.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts index a5132f1ab93..a1b8e527395 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/Docker.ts @@ -164,11 +164,13 @@ export class Docker { retryDelay: axiosRetry.exponentialDelay, shouldResetTimeout: true, retryCondition: error => { - const tooManyRequests = - error && error.response && error.response.status === 429; + const tooManyRequests = !!( + error && error.response && error.response.status === 429 + ); - return !!( - axiosRetry.isNetworkOrIdempotentRequestError(error) || tooManyRequests + return ( + axiosRetry.isNetworkOrIdempotentRequestError(error) || + tooManyRequests ); } }); From f28cda9d37e944f136e68a8ec9938b26e2fd656d Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 21:29:44 -0400 Subject: [PATCH 23/35] Note where `events: any` comes from --- .../src/compilerSupplier/loadingStrategies/VersionRange.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts index 2e51a98da3e..913549c0b67 100644 --- a/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts +++ b/packages/compile-solidity/src/compilerSupplier/loadingStrategies/VersionRange.ts @@ -12,7 +12,7 @@ import { NoVersionError, NoRequestError } from "../errors"; export class VersionRange { private config: { - events: any; + events: any; // represents a @truffle/events instance, which lacks types compilerRoots: string[]; }; From e9e5f6843da0904d28e7eff572561926f0323765 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 21:42:38 -0400 Subject: [PATCH 24/35] Add type annotations to exported functions --- .../compilerSupplier/normalizeSolcVersion.ts | 2 +- .../src/compilerSupplier/rangeUtils.ts | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts b/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts index d043740954a..a415b76addb 100644 --- a/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts +++ b/packages/compile-solidity/src/compilerSupplier/normalizeSolcVersion.ts @@ -2,7 +2,7 @@ * Convert a full version string (possibly with commit information, etc.) into * a canonical short-form semver version (x.y.z) */ -export const normalizeSolcVersion = (input) => { +export const normalizeSolcVersion = (input: string | Buffer): string => { const version = String(input); return version.split(":")[1].trim(); }; diff --git a/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts b/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts index 4e0914f5fb0..9bc410d6a70 100644 --- a/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts +++ b/packages/compile-solidity/src/compilerSupplier/rangeUtils.ts @@ -8,7 +8,7 @@ import { CompilerSupplier } from "./index"; * takes a version string which may be native or local, and resolves * it to one which is (presumably) either a version or a version range */ -export function resolveToRange(version) { +export function resolveToRange(version?: string): string { if (!version) { return CompilerSupplier.getDefaultVersion(); } @@ -26,20 +26,25 @@ export function resolveToRange(version) { /** * parameter range may be either an individual version or a range */ -export function rangeContainsAtLeast(range, comparisonVersion) { +export function rangeContainsAtLeast( + range: string, + comparisonVersion: string +): boolean { //the following line works with prereleases - const individualAtLeast = + const individualAtLeast = !!( semver.valid(range, { loose: true }) && semver.gte(range, comparisonVersion, { includePrerelease: true, loose: true - }); + }) + ); //the following line doesn't, despite the flag, but does work with version ranges - const rangeAtLeast = + const rangeAtLeast = !!( semver.validRange(range, { loose: true }) && !semver.gtr(comparisonVersion, range, { includePrerelease: true, loose: true - }); //intersects will throw if given undefined so must ward against + }) //intersects will throw if given undefined so must ward against + ); return individualAtLeast || rangeAtLeast; } From 54df06c85fa82bfd3c315e962c9ea4d9533980f4 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 12 Aug 2021 21:52:30 -0400 Subject: [PATCH 25/35] Use a manky type coercion, not ts-expect-error --- .../compile-solidity/src/compilerSupplier/observeListeners.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/compile-solidity/src/compilerSupplier/observeListeners.ts b/packages/compile-solidity/src/compilerSupplier/observeListeners.ts index 5213a37c75c..ee936613398 100644 --- a/packages/compile-solidity/src/compilerSupplier/observeListeners.ts +++ b/packages/compile-solidity/src/compilerSupplier/observeListeners.ts @@ -24,8 +24,7 @@ class ObservedListeners { cleanup() { for (const eventName in this.listeners) { const marked = this.listeners[eventName]; - // @ts-expect-error since eventName: string - for (const listener of process.listeners(eventName)) { + for (const listener of process.listeners(eventName as NodeJS.Signals)) { if (!marked.has(listener)) { process.removeListener(eventName, listener); } From dbe93a49923aae58dd12520dcb2a8264d8de4059 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 13 Aug 2021 14:27:46 -0400 Subject: [PATCH 26/35] Add missing types --- packages/compile-solidity/src/compilerSupplier/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/compile-solidity/src/compilerSupplier/index.ts b/packages/compile-solidity/src/compilerSupplier/index.ts index 09445413bc0..121f445611c 100644 --- a/packages/compile-solidity/src/compilerSupplier/index.ts +++ b/packages/compile-solidity/src/compilerSupplier/index.ts @@ -9,15 +9,15 @@ const defaultSolcVersion = "0.5.16"; export class CompilerSupplier { private events: any; private version: string; - private docker: unknown; - private compilerRoots: unknown; + private docker: boolean; + private compilerRoots: string[]; private strategyOptions: Partial<{ version: string; docker: boolean; compilerRoots: string[]; events: any; // represents a @truffle/events instance, which lacks types spawn: { - maxBuffer: number + maxBuffer: number; }; }>; From 10e4820778973400d3f9e09354da4bdd03d8eed4 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Tue, 3 Aug 2021 19:30:36 -0400 Subject: [PATCH 27/35] Add CompilerSupplier support in compile-common --- .../src/compilerSupplier/constructor.ts | 35 +++++ .../src/compilerSupplier/index.ts | 3 + .../src/compilerSupplier/results.ts | 9 ++ .../src/compilerSupplier/strategy.ts | 106 +++++++++++++ .../src/compilerSupplier/supplier.ts | 139 ++++++++++++++++++ packages/compile-common/src/index.ts | 1 + 6 files changed, 293 insertions(+) create mode 100644 packages/compile-common/src/compilerSupplier/constructor.ts create mode 100644 packages/compile-common/src/compilerSupplier/index.ts create mode 100644 packages/compile-common/src/compilerSupplier/results.ts create mode 100644 packages/compile-common/src/compilerSupplier/strategy.ts create mode 100644 packages/compile-common/src/compilerSupplier/supplier.ts diff --git a/packages/compile-common/src/compilerSupplier/constructor.ts b/packages/compile-common/src/compilerSupplier/constructor.ts new file mode 100644 index 00000000000..d61865d60cf --- /dev/null +++ b/packages/compile-common/src/compilerSupplier/constructor.ts @@ -0,0 +1,35 @@ +export namespace Constructor { + /** + * Type-level description for a particular constructor + */ + export type Specification = { + /** + * Options argument for specified constructor + */ + options: any; + + /** + * Output object created by specified constructor + */ + result: any; + }; + + /** + * Getter type for constructor arg + */ + export type Options = S["options"]; + + /** + * Getter type for constructed object + */ + export type Result = S["result"]; +} + +/** + * An object of type Constructor is a JS constructor (or "class") that + * takes a sole argument (of the specified "options" type), and instantiates + * a new object in memory (of the specified "result" type). + */ +export type Constructor = new ( + options: Constructor.Options +) => Constructor.Result; diff --git a/packages/compile-common/src/compilerSupplier/index.ts b/packages/compile-common/src/compilerSupplier/index.ts new file mode 100644 index 00000000000..6f01e9a897d --- /dev/null +++ b/packages/compile-common/src/compilerSupplier/index.ts @@ -0,0 +1,3 @@ +export { Results } from "./results"; +export { Strategy, BaseStrategy } from "./strategy"; +export { Supplier, forDefinition } from "./supplier"; diff --git a/packages/compile-common/src/compilerSupplier/results.ts b/packages/compile-common/src/compilerSupplier/results.ts new file mode 100644 index 00000000000..7ca3ec02980 --- /dev/null +++ b/packages/compile-common/src/compilerSupplier/results.ts @@ -0,0 +1,9 @@ +export namespace Results { + export type Specification = { + load: any; + list: any; + }; + + export type Load = S["load"]; + export type List = S["list"]; +} diff --git a/packages/compile-common/src/compilerSupplier/strategy.ts b/packages/compile-common/src/compilerSupplier/strategy.ts new file mode 100644 index 00000000000..2cb6362e115 --- /dev/null +++ b/packages/compile-common/src/compilerSupplier/strategy.ts @@ -0,0 +1,106 @@ +import type { Constructor as FreeConstructor } from "./constructor"; +import type { Results as FreeResults } from "./results"; + +export namespace Strategy { + /** + * Type-level description of a compiler supplier strategy + */ + export type Specification = { + constructor: Omit; + results: FreeResults.Specification; + allowsLoadingSpecificVersion: boolean; + allowsListingVersions: boolean; + }; + + /** + * Scoped re-exports of Constructor, using Strategy.Specification as + * generic param + */ + export namespace Constructor { + /** + * Getter for the specified constructor, intersected with fixed result + * type of Strategy + */ + export type Specification = + S["constructor"] & { result: Strategy; }; + + /** + * Constructor options argument for a specified strategy + */ + export type Options = FreeConstructor.Options< + Strategy.Constructor.Specification + >; + } + + /** + * Type representing a constructor for specified strategy + */ + export type Constructor = FreeConstructor< + Strategy.Constructor.Specification + >; + + /** + * Results types for a particular strategy + */ + export namespace Results { + export type Specification = + S["results"]; + + export type Load = FreeResults.Load< + Strategy.Results.Specification + >; + + export type List = FreeResults.List< + Strategy.Results.Specification + >; + } + + /** + * Getter type for whether specified strategy allows argument to load() + */ + export type AllowsLoadingSpecificVersion< + S extends Strategy.Specification + > = S["allowsLoadingSpecificVersion"]; + + /** + * Getter type for whether specified strategy provides version listing + */ + export type AllowsListingVersions< + S extends Strategy.Specification + > = S["allowsListingVersions"]; + +} + +/** + * An object of type Strategy provides version loading and possibly version + * listing functionality + */ +export type Strategy = BaseStrategy & + (true extends Strategy.AllowsLoadingSpecificVersion + ? { + load(version?: string): Strategy.Results.Load; + } + : { + load(): Strategy.Results.Load; + }) & + (true extends Strategy.AllowsListingVersions + ? { + list(): Strategy.Results.List; + } + : {}); + +/** + * An object of BaseStrategy allows loading an unspecified version, plus + * type guards to determine additional functionality + */ +export interface BaseStrategy { + load(): Strategy.Results.Load; + + allowsLoadingSpecificVersion(): this is this & { + load(version?: string): Strategy.Results.Load; + }; + + allowsListingVersions(): this is { + list(): Strategy.Results.List; + }; +} diff --git a/packages/compile-common/src/compilerSupplier/supplier.ts b/packages/compile-common/src/compilerSupplier/supplier.ts new file mode 100644 index 00000000000..46cee6aceaa --- /dev/null +++ b/packages/compile-common/src/compilerSupplier/supplier.ts @@ -0,0 +1,139 @@ +import type { Strategy as FreeStrategy, BaseStrategy } from "./strategy"; +import type { Results as FreeResults } from "./results"; + +export namespace Supplier { + /** + * Type-level description of a supplier + */ + export type Specification = { + options: any; + results: FreeResults.Specification; + strategies: { + [strategyName: string]: Omit; + }; + }; + + /** + * Getter type for the options argument passed to the constructor for one of + * the named strategies for a specified supplier + */ + export type Options< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = S["options"] & Supplier.Strategy.Constructor.Options; + + export namespace Results { + /** + * Type-level specification of method results for specified supplier; + * used in conjunction with each strategy individually + */ + export type Specification = S["results"]; + } + + export namespace Strategies { + export type Specification = S["strategies"]; + } + + /** + * A type representing one of the strategy names for specified supplier + */ + export type StrategyName = string & + keyof Supplier.Strategies.Specification; + + export namespace Strategy { + /** + * Type-level specification of one of the named strategies for specified + * supplier; joins specified supplier results type + */ + export type Specification< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = Supplier.Strategies.Specification[N] & { + results: Supplier.Results.Specification + }; + + export namespace Constructor { + /** + * Type-level specification of the constructor for one of the named + * strategies for a specified supplier + */ + export type Specification< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = FreeStrategy.Constructor>; + + /** + * Getter type for options argument passed to the constructor for one of + * the named strategies for a specified supplier + */ + export type Options< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = { + [K in N]: FreeStrategy.Constructor.Options< + Supplier.Strategy.Specification + > + }[N]; + } + + /** + * An object of Constructor is a JS constructor (or "class") for one + * of the named strategies for specified supplier + */ + export type Constructor< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = FreeStrategy.Constructor>; + } + + /** + * An object of type Definition comprises constructors for each of the + * specified strategies, as well as a method to determine the name of which + * strategy to use for a given set of input options + */ + export type Definition = { + determineStrategy( + options: Options> + ): Supplier.StrategyName; + + strategyConstructors: { + [N in Supplier.StrategyName]: Supplier.Strategy.Constructor; + }; + }; +} + +/** + * An object of type Supplier provides an interface for loading and + * possibly listing versions of the compiler + * + * @dev for known strategy names, this computes the method signature of + * `load()` according to the corresponding strategy specification; + * + * for the default of "any strategy name", this type will resolve to an + * interface that may require the use of defined type guards + * (e.g. `if(supplier.allowsListingVersions()) { supplier.list(); }`) + */ +export type Supplier< + S extends Supplier.Specification, + N extends Supplier.StrategyName = Supplier.StrategyName +> = Supplier.StrategyName extends N + ? BaseStrategy> + : FreeStrategy>; + +/** + * Given the definition of specified supplier, create a function that + * determines+constructs a supplier strategy for a given set of options. + */ +export const forDefinition = ({ + determineStrategy, + strategyConstructors +}: Supplier.Definition) => >( + options: Supplier.Strategy.Constructor.Options +): Supplier => { + const strategyName = determineStrategy(options); + const Strategy = strategyConstructors[strategyName]; + + // @ts-ignore since we can't figure out N from inside + const supplier: Supplier = new Strategy(options); + return supplier; +}; diff --git a/packages/compile-common/src/index.ts b/packages/compile-common/src/index.ts index 9ab9eacc465..4938508761d 100644 --- a/packages/compile-common/src/index.ts +++ b/packages/compile-common/src/index.ts @@ -1,4 +1,5 @@ export { Profiler } from "./profiler"; +export * as CompilerSupplier from "./compilerSupplier"; export * as Shims from "./shims"; export * as Sources from "./sources"; export * as Errors from "./errors"; From a59cef235718021a01aa81b9af3c7e86f61e2100 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Wed, 11 Aug 2021 13:35:19 -0400 Subject: [PATCH 28/35] Make dedicated @truffle/supplier package --- .../src/compilerSupplier/index.ts | 3 - packages/compile-common/src/index.ts | 1 - packages/supplier/.gitignore | 2 + packages/supplier/README.md | 95 +++++++++++++++++++ packages/supplier/package.json | 46 +++++++++ .../src}/constructor.ts | 0 packages/supplier/src/index.ts | 10 ++ packages/supplier/src/mixin.ts | 23 +++++ .../src}/results.ts | 0 .../src}/strategy.ts | 2 +- packages/supplier/src/supplier.test.ts | 45 +++++++++ .../src}/supplier.ts | 2 +- packages/supplier/test-d/supplier.ts | 39 ++++++++ packages/supplier/test/.gitkeep | 0 packages/supplier/test/example/index.ts | 30 ++++++ packages/supplier/test/example/native.ts | 29 ++++++ .../supplier/test/example/remote-soljson.ts | 33 +++++++ packages/supplier/test/example/types.ts | 9 ++ packages/supplier/test/setup.js | 1 + packages/supplier/tsconfig.json | 34 +++++++ yarn.lock | 31 +++++- 21 files changed, 428 insertions(+), 7 deletions(-) delete mode 100644 packages/compile-common/src/compilerSupplier/index.ts create mode 100644 packages/supplier/.gitignore create mode 100644 packages/supplier/README.md create mode 100644 packages/supplier/package.json rename packages/{compile-common/src/compilerSupplier => supplier/src}/constructor.ts (100%) create mode 100644 packages/supplier/src/index.ts create mode 100644 packages/supplier/src/mixin.ts rename packages/{compile-common/src/compilerSupplier => supplier/src}/results.ts (100%) rename packages/{compile-common/src/compilerSupplier => supplier/src}/strategy.ts (97%) create mode 100644 packages/supplier/src/supplier.test.ts rename packages/{compile-common/src/compilerSupplier => supplier/src}/supplier.ts (98%) create mode 100644 packages/supplier/test-d/supplier.ts create mode 100644 packages/supplier/test/.gitkeep create mode 100644 packages/supplier/test/example/index.ts create mode 100644 packages/supplier/test/example/native.ts create mode 100644 packages/supplier/test/example/remote-soljson.ts create mode 100644 packages/supplier/test/example/types.ts create mode 100644 packages/supplier/test/setup.js create mode 100644 packages/supplier/tsconfig.json diff --git a/packages/compile-common/src/compilerSupplier/index.ts b/packages/compile-common/src/compilerSupplier/index.ts deleted file mode 100644 index 6f01e9a897d..00000000000 --- a/packages/compile-common/src/compilerSupplier/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Results } from "./results"; -export { Strategy, BaseStrategy } from "./strategy"; -export { Supplier, forDefinition } from "./supplier"; diff --git a/packages/compile-common/src/index.ts b/packages/compile-common/src/index.ts index 4938508761d..9ab9eacc465 100644 --- a/packages/compile-common/src/index.ts +++ b/packages/compile-common/src/index.ts @@ -1,5 +1,4 @@ export { Profiler } from "./profiler"; -export * as CompilerSupplier from "./compilerSupplier"; export * as Shims from "./shims"; export * as Sources from "./sources"; export * as Errors from "./errors"; diff --git a/packages/supplier/.gitignore b/packages/supplier/.gitignore new file mode 100644 index 00000000000..1eae0cf6700 --- /dev/null +++ b/packages/supplier/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/packages/supplier/README.md b/packages/supplier/README.md new file mode 100644 index 00000000000..1370eec3653 --- /dev/null +++ b/packages/supplier/README.md @@ -0,0 +1,95 @@ +# @truffle/supplier + +This package provides infrastructure for the rest of Truffle, defining the +concept of a "supplier", a component that downloads a specific version of some +desired code resource according to one or more strategies. (For example, +@truffle/compile-solidity comprises its "CompilerSupplier", which fetches +specific solc versions from the web / via docker / et al.) + +## Usage example + +1. Define a common "results" specification: what does `supplier.load()` and + `supplier.list()` return? + + e.g.: + + ```typescript + export type Compiler = { compile(): any }; + + export namespace Results { + export type Specification = { + load: Promise; + list: Promise; + }; + } + ``` + +2. Define one or more strategies. + + e.g.: + + ```typescript + import { Mixin } from "ts-mixer"; + + import { + Strategy, + AllowsLoadingSpecificVersion, + AllowsListingVersions + } from "@truffle/supplier"; + + import { Results } from "./types"; + + export namespace RemoteSoljson { + export type Specification = { + constructor: { + options: { strategy: "remote-soljson" }; + }; + results: Results.Specification; + allowsLoadingSpecificVersion: true; + allowsListingVersions: true; + }; + } + + export class RemoteSoljson + extends Mixin(AllowsLoadingSpecificVersion, AllowsListingVersions) + implements Strategy { + async load(_version?: string) { + return { compile: (): any => null }; + } + + async list(): Promise { + return []; + } + } + ``` + +3. Connect everything: + + e.g.: + + ```typescript + import { Supplier, forDefinition } from "@truffle/supplier"; + + import { Results } from "./types"; + import { RemoteSoljson } from "./remote-soljson"; + + export type Specification = { + results: Results.Specification; + options: {}; + strategies: { + "remote-soljson": RemoteSoljson.Specification; + }; + }; + + export const definition: Supplier.Definition = { + determineStrategy({ strategy }) { + return strategy; + }, + + strategyConstructors: { + "remote-soljson": RemoteSoljson + } + }; + + export const createCompilerSupplier = forDefinition(definition); + ``` diff --git a/packages/supplier/package.json b/packages/supplier/package.json new file mode 100644 index 00000000000..8448a8c882a --- /dev/null +++ b/packages/supplier/package.json @@ -0,0 +1,46 @@ +{ + "name": "@truffle/supplier", + "version": "0.1.0-0", + "description": "Infrastructure for downloading specific compiler versions, etc.", + "main": "lib/supplier.js", + "types": "dist/src/index.d.ts", + "author": "g. nicholas d'andrea ", + "license": "MIT", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/trufflesuite/truffle.git", + "directory": "packages/supplier" + }, + "scripts": { + "build": "ttsc", + "prepare": "yarn build", + "test": "yarn test:code && yarn test:types", + "test:code": "mocha -r ./test/setup.js **/*.test.ts", + "test:types": "yarn build && tsd", + "watch": "yarn build --watch" + }, + "bugs": { + "url": "https://github.com/trufflesuite/truffle/issues" + }, + "devDependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "12.12.21", + "mocha": "8.0.1", + "ts-node": "^9.0.0", + "tsd": "^0.17.0", + "ttypescript": "^1.5.12", + "typescript": "^4.2.0", + "typescript-transform-paths": "^3.2.1" + }, + "dependencies": { + "colors": "^1.4.0", + "debug": "^4.3.1", + "ts-mixer": "^6.0.0" + } +} diff --git a/packages/compile-common/src/compilerSupplier/constructor.ts b/packages/supplier/src/constructor.ts similarity index 100% rename from packages/compile-common/src/compilerSupplier/constructor.ts rename to packages/supplier/src/constructor.ts diff --git a/packages/supplier/src/index.ts b/packages/supplier/src/index.ts new file mode 100644 index 00000000000..b002f7da3e7 --- /dev/null +++ b/packages/supplier/src/index.ts @@ -0,0 +1,10 @@ +export { Constructor } from "./constructor"; +export { Results } from "./results"; +export { Strategy, BaseStrategy } from "./strategy"; +export { Supplier, forDefinition } from "./supplier"; +export { + AllowsLoadingSpecificVersion, + AllowsListingVersions, + ForbidsLoadingSpecificVersion, + ForbidsListingVersions +} from "./mixin"; diff --git a/packages/supplier/src/mixin.ts b/packages/supplier/src/mixin.ts new file mode 100644 index 00000000000..a3830d76402 --- /dev/null +++ b/packages/supplier/src/mixin.ts @@ -0,0 +1,23 @@ +export class AllowsLoadingSpecificVersion { + allowsLoadingSpecificVersion(): true { + return true; + } +} + +export class ForbidsLoadingSpecificVersion { + allowsLoadingSpecificVersion(): false { + return false; + } +} + +export class AllowsListingVersions { + allowsListingVersions(): true { + return true; + } +} + +export class ForbidsListingVersions { + allowsListingVersions(): false { + return false; + } +} diff --git a/packages/compile-common/src/compilerSupplier/results.ts b/packages/supplier/src/results.ts similarity index 100% rename from packages/compile-common/src/compilerSupplier/results.ts rename to packages/supplier/src/results.ts diff --git a/packages/compile-common/src/compilerSupplier/strategy.ts b/packages/supplier/src/strategy.ts similarity index 97% rename from packages/compile-common/src/compilerSupplier/strategy.ts rename to packages/supplier/src/strategy.ts index 2cb6362e115..eafb02716ee 100644 --- a/packages/compile-common/src/compilerSupplier/strategy.ts +++ b/packages/supplier/src/strategy.ts @@ -3,7 +3,7 @@ import type { Results as FreeResults } from "./results"; export namespace Strategy { /** - * Type-level description of a compiler supplier strategy + * Type-level description of a supplier strategy */ export type Specification = { constructor: Omit; diff --git a/packages/supplier/src/supplier.test.ts b/packages/supplier/src/supplier.test.ts new file mode 100644 index 00000000000..fadae3e6938 --- /dev/null +++ b/packages/supplier/src/supplier.test.ts @@ -0,0 +1,45 @@ +import assert from "assert"; + +import { createCompilerSupplier, Native, RemoteSoljson } from "test/example"; + +describe("Supplier.forDefinition", function () { + it("selects the correct strategy", async function () { + { + const supplier = createCompilerSupplier({ strategy: "native" }); + + assert.ok(supplier instanceof Native); + } + + { + const supplier = createCompilerSupplier({ strategy: "remote-soljson" }); + + assert.ok(supplier instanceof RemoteSoljson); + } + }); + + it("allows listing only when strategy allows it", async function() { + { + const supplier = createCompilerSupplier({ strategy: "native" }); + assert(!supplier.allowsListingVersions()); + + try { + // @ts-expect-error + await supplier.list(); + + assert.fail("Should have thrown error"); + } catch {} + } + + { + const supplier = createCompilerSupplier({ strategy: "remote-soljson" }); + + if (!supplier.allowsListingVersions()) { + throw new Error("Strategy should allow listing versions"); + } + + const versions = await supplier.list(); + + assert.deepEqual(versions, []); + } + }); +}); diff --git a/packages/compile-common/src/compilerSupplier/supplier.ts b/packages/supplier/src/supplier.ts similarity index 98% rename from packages/compile-common/src/compilerSupplier/supplier.ts rename to packages/supplier/src/supplier.ts index 46cee6aceaa..af8f40921e6 100644 --- a/packages/compile-common/src/compilerSupplier/supplier.ts +++ b/packages/supplier/src/supplier.ts @@ -104,7 +104,7 @@ export namespace Supplier { /** * An object of type Supplier provides an interface for loading and - * possibly listing versions of the compiler + * possibly listing versions of the supplied component * * @dev for known strategy names, this computes the method signature of * `load()` according to the corresponding strategy specification; diff --git a/packages/supplier/test-d/supplier.ts b/packages/supplier/test-d/supplier.ts new file mode 100644 index 00000000000..0ace5d0ca8c --- /dev/null +++ b/packages/supplier/test-d/supplier.ts @@ -0,0 +1,39 @@ +import { expectType, expectError } from "tsd"; + +import { Compiler, createCompilerSupplier } from "test/example"; + + +{ + const supplier = createCompilerSupplier<"remote-soljson">({ + strategy: "remote-soljson" + }); + + supplier.load("0.5.0"); + supplier.list(); +} + +{ + const supplier = createCompilerSupplier<"native">({ strategy: "native" }); + + expectType>(supplier.load()); + // errors: + expectError(supplier.load("0.5.0")); + expectError(supplier.list()); +} + +{ + const supplier = createCompilerSupplier({ strategy: "remote-soljson" }); + + expectType>(supplier.load()); + // errors: + expectError(supplier.load("0.5.0")); + expectError(supplier.list()); + + if (supplier.allowsLoadingSpecificVersion()) { + expectType>(supplier.load("0.1.0")); + } + + if (supplier.allowsListingVersions()) { + expectType>(supplier.list()); + } +} diff --git a/packages/supplier/test/.gitkeep b/packages/supplier/test/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/supplier/test/example/index.ts b/packages/supplier/test/example/index.ts new file mode 100644 index 00000000000..b993b68305e --- /dev/null +++ b/packages/supplier/test/example/index.ts @@ -0,0 +1,30 @@ +import { Supplier, forDefinition } from "@truffle/supplier"; + +import { Results } from "./types"; +import { Native } from "./native"; +import { RemoteSoljson } from "./remote-soljson"; + +export type Specification = { + results: Results.Specification; + options: {}; + strategies: { + "native": Native.Specification; + "remote-soljson": RemoteSoljson.Specification + } +}; + +export const definition: Supplier.Definition = { + determineStrategy({ strategy }) { + return strategy; + }, + + strategyConstructors: { + "native": Native, + "remote-soljson": RemoteSoljson + } +}; + +export const createCompilerSupplier = forDefinition(definition); + +export type { Compiler } from "./types"; +export { Native, RemoteSoljson }; diff --git a/packages/supplier/test/example/native.ts b/packages/supplier/test/example/native.ts new file mode 100644 index 00000000000..8c87745c511 --- /dev/null +++ b/packages/supplier/test/example/native.ts @@ -0,0 +1,29 @@ +import { Mixin } from "ts-mixer"; + +import { + Strategy, + ForbidsLoadingSpecificVersion, + ForbidsListingVersions +} from "@truffle/supplier"; + +import { Results } from "./types"; + +export namespace Native { + export type Specification = { + constructor: { + options: { strategy: "native" }; + }; + results: Results.Specification; + allowsLoadingSpecificVersion: false; + allowsListingVersions: false; + }; +} + +export class Native + extends Mixin(ForbidsLoadingSpecificVersion, ForbidsListingVersions) + implements Strategy +{ + async load() { + return { compile: (): any => null }; + } +} diff --git a/packages/supplier/test/example/remote-soljson.ts b/packages/supplier/test/example/remote-soljson.ts new file mode 100644 index 00000000000..8065513a409 --- /dev/null +++ b/packages/supplier/test/example/remote-soljson.ts @@ -0,0 +1,33 @@ +import { Mixin } from "ts-mixer"; + +import { + Strategy, + AllowsLoadingSpecificVersion, + AllowsListingVersions +} from "@truffle/supplier"; + +import { Results } from "./types"; + +export namespace RemoteSoljson { + export type Specification = { + constructor: { + options: { strategy: "remote-soljson" }; + }; + results: Results.Specification; + allowsLoadingSpecificVersion: true; + allowsListingVersions: true; + }; +} + +export class RemoteSoljson + extends Mixin(AllowsLoadingSpecificVersion, AllowsListingVersions) + implements Strategy +{ + async load(_version?: string) { + return { compile: (): any => null }; + } + + async list(): Promise { + return []; + } +} diff --git a/packages/supplier/test/example/types.ts b/packages/supplier/test/example/types.ts new file mode 100644 index 00000000000..509a0701d27 --- /dev/null +++ b/packages/supplier/test/example/types.ts @@ -0,0 +1,9 @@ +export type Compiler = { compile(): any }; + +export namespace Results { + export type Specification = { + load: Promise; + list: Promise; + }; +} + diff --git a/packages/supplier/test/setup.js b/packages/supplier/test/setup.js new file mode 100644 index 00000000000..30b828d7abe --- /dev/null +++ b/packages/supplier/test/setup.js @@ -0,0 +1 @@ +require("ts-node").register({ compiler: "ttypescript" }); diff --git a/packages/supplier/tsconfig.json b/packages/supplier/tsconfig.json new file mode 100644 index 00000000000..159928e7449 --- /dev/null +++ b/packages/supplier/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "declaration": true, + "target": "es2016", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "skipLibCheck": true, + "strictBindCallApply": true, + "strictNullChecks": true, + "downlevelIteration": true, + "allowSyntheticDefaultImports": true, + "outDir": "dist", + "baseUrl": ".", + "lib": ["es2017"], + "paths": { + "@truffle/supplier": ["./src"], + "@truffle/supplier/*": ["./src/*"], + "test/*": ["./test/*"] + }, + "rootDir": ".", + "types": ["node", "mocha"], + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typescript-transform-paths", "afterDeclarations": true } + ] + }, + "include": [ + "./src/**/*.ts", + "./test/**/*.ts" + ] +} diff --git a/yarn.lock b/yarn.lock index f7b6fddc0a2..89007ad91d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3828,6 +3828,11 @@ xhr "^2.2.0" xtend "^4.0.1" +"@tsd/typescript@~4.3.2": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@tsd/typescript/-/typescript-4.3.5.tgz#0e0669bbd82a399a06c825c22dc63d56debe70a2" + integrity sha512-Xwxv8bIwyI3ggPz9bwoWEoiaz79MJs+VGf27S1N2tapfDVo60Lz741j5diL9RwszZSXt6IkTAuw7Lai7jSXRJg== + "@types/accepts@*", "@types/accepts@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" @@ -27409,6 +27414,11 @@ ts-jest@^26.5.0: semver "7.x" yargs-parser "20.x" +ts-mixer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.0.tgz#4e631d3a36e3fa9521b973b132e8353bc7267f9f" + integrity sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ== + ts-node-dev@^1.0.0-pre.32: version "1.0.0-pre.43" resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.43.tgz#9084775060c0a4c0568e1670b249eabd0d4260d5" @@ -27496,6 +27506,18 @@ tsd@^0.15.1: read-pkg-up "^7.0.0" update-notifier "^4.1.0" +tsd@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.17.0.tgz#e5aa66d6598d0b66628784ecb4c0c27795b47317" + integrity sha512-+HUwya2NgoP/g9t2gRCC3I8VtGu65NgG9Lv75vNzMaxjMFo+0VXF9c4sj3remSzJYeBHLNKzWMbFOinPqrL20Q== + dependencies: + "@tsd/typescript" "~4.3.2" + eslint-formatter-pretty "^4.0.0" + globby "^11.0.1" + meow "^9.0.0" + path-exists "^4.0.0" + read-pkg-up "^7.0.0" + tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -27781,6 +27803,13 @@ typescript-transform-paths@^3.1.0: dependencies: minimatch "^3.0.4" +typescript-transform-paths@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-3.2.1.tgz#aa3f60ebf8c9eb4c4c07c96b91f6ee0a0db1b3f8" + integrity sha512-A2WRrELxKz4ivai9NO2lq7+H2peNZ9/ouSULSgrE0IpEa/sdam2k70aWn9BNb1rFUZnWn/MFXcSijvpa2GgwzQ== + dependencies: + minimatch "^3.0.4" + typescript-tuple@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2" @@ -27798,7 +27827,7 @@ typescript@^4.1.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.4.tgz#f058636e2f4f83f94ddaae07b20fd5e14598432f" integrity sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg== -typescript@^4.3.5: +typescript@^4.2.0, typescript@^4.3.5: version "4.3.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== From 5499b2e7911de5ca5584aa5d0b6b71ca7c4b198d Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Tue, 17 Aug 2021 20:40:55 -0400 Subject: [PATCH 29/35] Ensure all types get docstrings --- packages/supplier/src/constructor.ts | 26 ++-- packages/supplier/src/results.ts | 16 +++ packages/supplier/src/strategy.ts | 165 ++++++++++++++++-------- packages/supplier/src/supplier.ts | 180 +++++++++++++++++---------- 4 files changed, 251 insertions(+), 136 deletions(-) diff --git a/packages/supplier/src/constructor.ts b/packages/supplier/src/constructor.ts index d61865d60cf..9f610a3198b 100644 --- a/packages/supplier/src/constructor.ts +++ b/packages/supplier/src/constructor.ts @@ -1,35 +1,35 @@ +/** + * An object of type Constructor is a JS constructor (or "class") that + * takes a sole argument (of the specified "options" type), and instantiates + * a new object in memory (of the specified "result" type). + */ +export type Constructor = new ( + options: Constructor.Options +) => Constructor.Result; + export namespace Constructor { /** * Type-level description for a particular constructor */ export type Specification = { /** - * Options argument for specified constructor + * The type of the sole `options` argument passed to the constructor */ options: any; /** - * Output object created by specified constructor + * The type of the object the constructor creates */ result: any; }; /** - * Getter type for constructor arg + * The `options` type for the specified constructor. */ export type Options = S["options"]; /** - * Getter type for constructed object + * The type of the object created by the specified constructor. */ export type Result = S["result"]; } - -/** - * An object of type Constructor is a JS constructor (or "class") that - * takes a sole argument (of the specified "options" type), and instantiates - * a new object in memory (of the specified "result" type). - */ -export type Constructor = new ( - options: Constructor.Options -) => Constructor.Result; diff --git a/packages/supplier/src/results.ts b/packages/supplier/src/results.ts index 7ca3ec02980..cbf8fb56d5a 100644 --- a/packages/supplier/src/results.ts +++ b/packages/supplier/src/results.ts @@ -1,9 +1,25 @@ export namespace Results { export type Specification = { + /** + * When specifying results, the `load` type represents the return value for + * a `load()` method. + */ load: any; + + /** + * When specifying results, the `load` type represents the return value for + * a `list()` method. + */ list: any; }; + /** + * The type returned by `load()` according to the results specification. + */ export type Load = S["load"]; + + /** + * The type returned by `list()` according to the results specification. + */ export type List = S["list"]; } diff --git a/packages/supplier/src/strategy.ts b/packages/supplier/src/strategy.ts index eafb02716ee..3a2ca7ccb77 100644 --- a/packages/supplier/src/strategy.ts +++ b/packages/supplier/src/strategy.ts @@ -1,106 +1,163 @@ import type { Constructor as FreeConstructor } from "./constructor"; import type { Results as FreeResults } from "./results"; +/** + * An object of type Strategy provides version loading and possibly version + * listing functionality + * + * Note that a valid Strategy.Specification must include `true` or `false` for + * `allowsLoadingSpecificVersion` and `allowsListingVersions` types, otherwise + * Strategy may not be well-formed. These types are used as flags to + * determine the correct method signatures for the strategy. + */ +export type Strategy = BaseStrategy & + (true extends Strategy.AllowsLoadingSpecificVersion + ? { + load(version?: string): Strategy.Results.Load; + } + : { + load(): Strategy.Results.Load; + }) & + (true extends Strategy.AllowsListingVersions + ? { + list(): Strategy.Results.List; + } + : {}); + +/** + * An object of BaseStrategy allows loading an unspecified version, plus + * type guards to determine additional functionality + */ +export interface BaseStrategy { + load(): Strategy.Results.Load; + + allowsLoadingSpecificVersion(): this is this & { + load(version?: string): Strategy.Results.Load; + }; + + allowsListingVersions(): this is { + list(): Strategy.Results.List; + }; +} + export namespace Strategy { /** * Type-level description of a supplier strategy */ export type Specification = { + /** + * A strategy specification includes a constructor specification, minus + * the constructor specification's `result` property, since this module + * will inherently deal in constructors that result in an instance of + * the specified Strategy. + */ constructor: Omit; + + /** + * A strategy specification includes a `results` specification, + * defining the types returned for `load()` and `list()` methods. + */ results: FreeResults.Specification; + + /** + * A strategy specifies whether it allows loading specific versions. + * + * When specifying a strategy, this must be `true` or `false` explicitly. + */ allowsLoadingSpecificVersion: boolean; + + /** + * A strategy specifies whether it allows listing versions. + * + * When specifying a strategy, this must be `true` or `false` explicitly. + */ allowsListingVersions: boolean; }; + /** + * Type representing a constructor for specified strategy. + * + * This re-exports from the constructor module to use Strategy.Specification + * as root. + */ + export type Constructor = FreeConstructor< + Strategy.Constructor.Specification + >; + /** * Scoped re-exports of Constructor, using Strategy.Specification as * generic param */ export namespace Constructor { /** - * Getter for the specified constructor, intersected with fixed result - * type of Strategy + * Type-level specification of the constructor for a given strategy; + * + * Constructor `options` are taken as specified for the strategy, using + * an instance of the specified strategy as the result specification. */ - export type Specification = - S["constructor"] & { result: Strategy; }; + export type Specification< + S extends Strategy.Specification + > = S["constructor"] & { result: Strategy }; /** - * Constructor options argument for a specified strategy + * Type representing the `options` argument for constructing the specified + * strategy. + * + * This re-exports from the constructor module to use + * Strategy.Specification as the root. */ - export type Options = FreeConstructor.Options< - Strategy.Constructor.Specification - >; + export type Options< + S extends Strategy.Specification + > = FreeConstructor.Options>; } - /** - * Type representing a constructor for specified strategy - */ - export type Constructor = FreeConstructor< - Strategy.Constructor.Specification - >; - /** * Results types for a particular strategy */ export namespace Results { - export type Specification = - S["results"]; + /** + * Type-level specification of method results for specified strategy + */ + export type Specification = S["results"]; + /** + * Type returned by the strategy's `load()` method. + * + * This re-exports from the results module to use Strategy.Specification + * as the root. + */ export type Load = FreeResults.Load< Strategy.Results.Specification >; + /** + * Type returned by the strategy's `list()` method. + * + * This re-exports from the results module to use Strategy.Specification + * as the root. + */ export type List = FreeResults.List< Strategy.Results.Specification >; } /** - * Getter type for whether specified strategy allows argument to load() + * Whether the specified strategy allows loading specific versions. + * + * For a well-specified strategy, this will always be the type literal + * `true` or `false` (never the generalized `boolean`) */ export type AllowsLoadingSpecificVersion< S extends Strategy.Specification > = S["allowsLoadingSpecificVersion"]; /** - * Getter type for whether specified strategy provides version listing + * Whether the specified strategy allows listing known versions. + * + * For a well-specified strategy, this will always be the type literal + * `true` or `false` (never the generalized `boolean`) */ export type AllowsListingVersions< S extends Strategy.Specification > = S["allowsListingVersions"]; - -} - -/** - * An object of type Strategy provides version loading and possibly version - * listing functionality - */ -export type Strategy = BaseStrategy & - (true extends Strategy.AllowsLoadingSpecificVersion - ? { - load(version?: string): Strategy.Results.Load; - } - : { - load(): Strategy.Results.Load; - }) & - (true extends Strategy.AllowsListingVersions - ? { - list(): Strategy.Results.List; - } - : {}); - -/** - * An object of BaseStrategy allows loading an unspecified version, plus - * type guards to determine additional functionality - */ -export interface BaseStrategy { - load(): Strategy.Results.Load; - - allowsLoadingSpecificVersion(): this is this & { - load(version?: string): Strategy.Results.Load; - }; - - allowsListingVersions(): this is { - list(): Strategy.Results.List; - }; } diff --git a/packages/supplier/src/supplier.ts b/packages/supplier/src/supplier.ts index af8f40921e6..72b9d32a197 100644 --- a/packages/supplier/src/supplier.ts +++ b/packages/supplier/src/supplier.ts @@ -1,21 +1,108 @@ import type { Strategy as FreeStrategy, BaseStrategy } from "./strategy"; import type { Results as FreeResults } from "./results"; +/** + * Given the definition of specified supplier, create a function that + * determines+constructs a supplier strategy for a given set of options. + */ +export const forDefinition = ({ + determineStrategy, + strategyConstructors +}: Supplier.Definition): CreateSupplier => < + N extends Supplier.StrategyName +>( + options: Supplier.Strategy.Constructor.Options +): Supplier => { + const strategyName = determineStrategy(options); + const Strategy = strategyConstructors[strategyName]; + + // @ts-ignore since we can't figure out N from inside + const supplier: Supplier = new Strategy(options); + return supplier; +}; + +/** + * A function that constructs a Supplier + */ +export type CreateSupplier = < + N extends Supplier.StrategyName +>( + options: Supplier.Strategy.Constructor.Options +) => Supplier; + +/** + * An object of type Supplier provides an interface for loading and + * possibly listing versions of the supplied component + * + * @dev for known strategy names, this computes the method signature of + * `load()` according to the corresponding strategy specification; + * + * for the default of "any strategy name", this type will resolve to an + * interface that may require the use of defined type guards + * (e.g. `if(supplier.allowsListingVersions()) { supplier.list(); }`) + */ +export type Supplier< + S extends Supplier.Specification, + N extends Supplier.StrategyName = Supplier.StrategyName +> = Supplier.StrategyName extends N + ? BaseStrategy> + : FreeStrategy>; + export namespace Supplier { /** - * Type-level description of a supplier + * Type-level description of a supplier. */ export type Specification = { + /** + * When creating a supplier, the `options` type represents whatever data + * is necessary for creating an instance of the specified supplier. + * + * N.B. that this type is distinct from any strategy-specific options type: + * `createSupplier()` requires an options argument that conforms to the + * intersection of this `options` type, as well as the options type for at + * least one strategy. + */ + options: any; + + /** + * A supplier specification includes a unified `results` specification, + * which defines the `supplier.load()` and `supplier.list()` return types + * for all strategies. + */ results: FreeResults.Specification; + + /** + * A supplier specification includes specifications for all strategies by + * name. + */ strategies: { - [strategyName: string]: Omit; + [strategyName: string]: FreeStrategy.Specification; + }; + }; + + /** + * An object of type Definition comprises constructors for each of the + * specified strategies, as well as a method to determine the name of which + * strategy to use for a given set of input options + */ + export type Definition = { + determineStrategy( + options: Options> + ): Supplier.StrategyName; + + strategyConstructors: { + [N in Supplier.StrategyName]: Supplier.Strategy.Constructor; }; }; /** - * Getter type for the options argument passed to the constructor for one of - * the named strategies for a specified supplier + * Given a supplier specification and a specific strategy name, + * `Options` represents the `options` type used to create a supplier + * using that strategy. + * + * This type intersects the `options` for the specific strategy with the + * `options` type for the supplier generally. */ export type Options< S extends Supplier.Specification, @@ -31,7 +118,12 @@ export namespace Supplier { } export namespace Strategies { - export type Specification = S["strategies"]; + /** + * Type-level specification of all strategies for a specified supplier + */ + export type Specification< + S extends Supplier.Specification + > = S["strategies"]; } /** @@ -49,9 +141,18 @@ export namespace Supplier { S extends Supplier.Specification, N extends Supplier.StrategyName > = Supplier.Strategies.Specification[N] & { - results: Supplier.Results.Specification + results: Supplier.Results.Specification; }; + /** + * An object of type Constructor is a JS constructor (or "class") for + * one of the named strategies for specified supplier + */ + export type Constructor< + S extends Supplier.Specification, + N extends Supplier.StrategyName + > = FreeStrategy.Constructor>; + export namespace Constructor { /** * Type-level specification of the constructor for one of the named @@ -63,8 +164,9 @@ export namespace Supplier { > = FreeStrategy.Constructor>; /** - * Getter type for options argument passed to the constructor for one of - * the named strategies for a specified supplier + * Given a supplier specification and a specific strategy name, + * `Options` represents the `options` type used to construct that + * strategy specifically. */ export type Options< S extends Supplier.Specification, @@ -72,68 +174,8 @@ export namespace Supplier { > = { [K in N]: FreeStrategy.Constructor.Options< Supplier.Strategy.Specification - > + >; }[N]; } - - /** - * An object of Constructor is a JS constructor (or "class") for one - * of the named strategies for specified supplier - */ - export type Constructor< - S extends Supplier.Specification, - N extends Supplier.StrategyName - > = FreeStrategy.Constructor>; } - - /** - * An object of type Definition comprises constructors for each of the - * specified strategies, as well as a method to determine the name of which - * strategy to use for a given set of input options - */ - export type Definition = { - determineStrategy( - options: Options> - ): Supplier.StrategyName; - - strategyConstructors: { - [N in Supplier.StrategyName]: Supplier.Strategy.Constructor; - }; - }; } - -/** - * An object of type Supplier provides an interface for loading and - * possibly listing versions of the supplied component - * - * @dev for known strategy names, this computes the method signature of - * `load()` according to the corresponding strategy specification; - * - * for the default of "any strategy name", this type will resolve to an - * interface that may require the use of defined type guards - * (e.g. `if(supplier.allowsListingVersions()) { supplier.list(); }`) - */ -export type Supplier< - S extends Supplier.Specification, - N extends Supplier.StrategyName = Supplier.StrategyName -> = Supplier.StrategyName extends N - ? BaseStrategy> - : FreeStrategy>; - -/** - * Given the definition of specified supplier, create a function that - * determines+constructs a supplier strategy for a given set of options. - */ -export const forDefinition = ({ - determineStrategy, - strategyConstructors -}: Supplier.Definition) => >( - options: Supplier.Strategy.Constructor.Options -): Supplier => { - const strategyName = determineStrategy(options); - const Strategy = strategyConstructors[strategyName]; - - // @ts-ignore since we can't figure out N from inside - const supplier: Supplier = new Strategy(options); - return supplier; -}; From bdd85cc7271f9af290f121ad0b32bb8bec753cdc Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 19 Aug 2021 15:24:05 -0400 Subject: [PATCH 30/35] Extract load/list interfaces --- packages/supplier/src/strategy.ts | 58 ++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/supplier/src/strategy.ts b/packages/supplier/src/strategy.ts index 3a2ca7ccb77..bf39f6181c5 100644 --- a/packages/supplier/src/strategy.ts +++ b/packages/supplier/src/strategy.ts @@ -12,32 +12,19 @@ import type { Results as FreeResults } from "./results"; */ export type Strategy = BaseStrategy & (true extends Strategy.AllowsLoadingSpecificVersion - ? { - load(version?: string): Strategy.Results.Load; - } - : { - load(): Strategy.Results.Load; - }) & - (true extends Strategy.AllowsListingVersions - ? { - list(): Strategy.Results.List; - } - : {}); + ? Strategy.SpecificVersionLoader + : Strategy.BaseLoader) & + (true extends Strategy.AllowsListingVersions ? Strategy.Lister : {}); /** * An object of BaseStrategy allows loading an unspecified version, plus * type guards to determine additional functionality */ -export interface BaseStrategy { - load(): Strategy.Results.Load; - - allowsLoadingSpecificVersion(): this is this & { - load(version?: string): Strategy.Results.Load; - }; - - allowsListingVersions(): this is { - list(): Strategy.Results.List; - }; +export interface BaseStrategy + extends Strategy.BaseLoader { + allowsLoadingSpecificVersion(): this is this & + Strategy.SpecificVersionLoader; + allowsListingVersions(): this is this & Strategy.Lister; } export namespace Strategy { @@ -160,4 +147,33 @@ export namespace Strategy { export type AllowsListingVersions< S extends Strategy.Specification > = S["allowsListingVersions"]; + + /** + * An object adhering to the BaseLoader interface provides the `load()` + * method. + * + * This type is used in the construction of the root Strategy type. + */ + export interface BaseLoader { + load(): Strategy.Results.Load; + } + + /** + * An object adhering to the SpecificVersionLoader interface provides the + * `load(version?: string)` method + * + * This type is used in the construction of the root Strategy type. + */ + export interface SpecificVersionLoader { + load(version?: string): Strategy.Results.Load; + } + + /** + * An object adhering to the Lister interface provides the `list()` method + * + * This type is used in the construction of the root Strategy type. + */ + export interface Lister { + list(): Strategy.Results.List; + } } From 25e7457dfe21a02593d2abbd46d4daec96c3bb67 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 20 Aug 2021 16:19:45 -0400 Subject: [PATCH 31/35] Allow optional N in Supplier.Options --- packages/supplier/src/supplier.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/supplier/src/supplier.ts b/packages/supplier/src/supplier.ts index 72b9d32a197..243c5711fd3 100644 --- a/packages/supplier/src/supplier.ts +++ b/packages/supplier/src/supplier.ts @@ -87,9 +87,7 @@ export namespace Supplier { * strategy to use for a given set of input options */ export type Definition = { - determineStrategy( - options: Options> - ): Supplier.StrategyName; + determineStrategy(options: Options): Supplier.StrategyName; strategyConstructors: { [N in Supplier.StrategyName]: Supplier.Strategy.Constructor; @@ -106,7 +104,7 @@ export namespace Supplier { */ export type Options< S extends Supplier.Specification, - N extends Supplier.StrategyName + N extends Supplier.StrategyName = Supplier.StrategyName > = S["options"] & Supplier.Strategy.Constructor.Options; export namespace Results { From 78e36ea8e13207e3b73b5963791ed116e1c5a55c Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 20 Aug 2021 16:26:54 -0400 Subject: [PATCH 32/35] Fix dependencies - Remove unused colors - Move ts-mixer to devDependencies --- packages/supplier/package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/supplier/package.json b/packages/supplier/package.json index 8448a8c882a..7b4b6b5d533 100644 --- a/packages/supplier/package.json +++ b/packages/supplier/package.json @@ -32,6 +32,7 @@ "@types/mocha": "^5.2.7", "@types/node": "12.12.21", "mocha": "8.0.1", + "ts-mixer": "^6.0.0", "ts-node": "^9.0.0", "tsd": "^0.17.0", "ttypescript": "^1.5.12", @@ -39,8 +40,6 @@ "typescript-transform-paths": "^3.2.1" }, "dependencies": { - "colors": "^1.4.0", - "debug": "^4.3.1", - "ts-mixer": "^6.0.0" + "debug": "^4.3.1" } } From 379ffeeca87b54a5ba5b9b08c4ee6ee69523b9f4 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 20 Aug 2021 16:39:39 -0400 Subject: [PATCH 33/35] Explain Supplier generic params --- packages/supplier/src/supplier.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/supplier/src/supplier.ts b/packages/supplier/src/supplier.ts index 243c5711fd3..45dcc2f97dd 100644 --- a/packages/supplier/src/supplier.ts +++ b/packages/supplier/src/supplier.ts @@ -22,17 +22,22 @@ export const forDefinition = ({ }; /** - * A function that constructs a Supplier + * A function of type CreateSupplier (for a given Supplier.Specification S) + * accepts options for a particular strategy and constructs an instance of + * the specified Supplier. */ export type CreateSupplier = < N extends Supplier.StrategyName >( - options: Supplier.Strategy.Constructor.Options + options: Supplier.Options ) => Supplier; /** * An object of type Supplier provides an interface for loading and - * possibly listing versions of the supplied component + * possibly listing versions of the supplied component, where S is a particular + * Supplier.Specification type and where N is [optionally] a particular + * Supplier.StrategyName. When N is omitted, Supplier represents a + * supplier that uses any specified strategy. * * @dev for known strategy names, this computes the method signature of * `load()` according to the corresponding strategy specification; From bdb8ec0a0729da2d7ce7e4974d37300eba7c9b71 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 20 Aug 2021 16:52:24 -0400 Subject: [PATCH 34/35] Clean up generics a bit - Ensure generic parameters are documented - Extend optionality of N through a few types where appropriate - Define a standalone `Supplier.Strategy` type --- packages/supplier/src/supplier.ts | 34 ++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/supplier/src/supplier.ts b/packages/supplier/src/supplier.ts index 45dcc2f97dd..105fd0a3c60 100644 --- a/packages/supplier/src/supplier.ts +++ b/packages/supplier/src/supplier.ts @@ -49,9 +49,7 @@ export type CreateSupplier = < export type Supplier< S extends Supplier.Specification, N extends Supplier.StrategyName = Supplier.StrategyName -> = Supplier.StrategyName extends N - ? BaseStrategy> - : FreeStrategy>; +> = Supplier.Strategy; export namespace Supplier { /** @@ -92,7 +90,7 @@ export namespace Supplier { * strategy to use for a given set of input options */ export type Definition = { - determineStrategy(options: Options): Supplier.StrategyName; + determineStrategy(options: Supplier.Options): Supplier.StrategyName; strategyConstructors: { [N in Supplier.StrategyName]: Supplier.Strategy.Constructor; @@ -135,14 +133,40 @@ export namespace Supplier { export type StrategyName = string & keyof Supplier.Strategies.Specification; + /** + * An object of type Supplier.Strategy adheres to the common BaseStrategy + * interface for any one of the strategies specified in the + * Supplier.Specification S. + * + * An object of type Supplier.Strategy (for a given string literal + * Supplier.StrategyName type N) adheres to the interface provided by + * that particular strategy. + * + * This type determines which of BaseStrategy and FreeStrategy to re-export, + * based on whether or not N is specified as the default "any strategy name" + * or specified as a particular strategy name. + */ + export type Strategy< + S extends Supplier.Specification, + N extends Supplier.StrategyName = Supplier.StrategyName + > = + // check for default case where N is equal to Supplier.StrategyName + // [logic: if (A extends B) and (B extends A), then (A equals B)] + Supplier.StrategyName extends N + ? BaseStrategy> + : FreeStrategy>; + export namespace Strategy { /** * Type-level specification of one of the named strategies for specified * supplier; joins specified supplier results type + * + * In cases where N is omitted, this represents the type union of each + * Strategy.Specification for all strategies of the specified supplier. */ export type Specification< S extends Supplier.Specification, - N extends Supplier.StrategyName + N extends Supplier.StrategyName = Supplier.StrategyName > = Supplier.Strategies.Specification[N] & { results: Supplier.Results.Specification; }; From 710afa7f0e295d61e56beb693d8a966f57f735c0 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Fri, 20 Aug 2021 17:00:32 -0400 Subject: [PATCH 35/35] Add mixin-free usage example to README --- packages/supplier/README.md | 69 +++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/packages/supplier/README.md b/packages/supplier/README.md index 1370eec3653..1b2ae792cd4 100644 --- a/packages/supplier/README.md +++ b/packages/supplier/README.md @@ -11,7 +11,9 @@ specific solc versions from the web / via docker / et al.) 1. Define a common "results" specification: what does `supplier.load()` and `supplier.list()` return? - e.g.: +

+

+ See example results specification ```typescript export type Compiler = { compile(): any }; @@ -24,9 +26,18 @@ specific solc versions from the web / via docker / et al.) } ``` +
+

+ 2. Define one or more strategies. - e.g.: +

+

+ + See example strategy specification (using provided mixin classes with + ts-mixer + package). + ```typescript import { Mixin } from "ts-mixer"; @@ -63,9 +74,58 @@ specific solc versions from the web / via docker / et al.) } ``` +
+

+ +

+

+ + See example strategy specification (without using mixins) + + + ```typescript + import { Strategy } from "@truffle/supplier"; + + import { Results } from "./types"; + + export namespace RemoteSoljson { + export type Specification = { + constructor: { + options: { strategy: "remote-soljson" }; + }; + results: Results.Specification; + allowsLoadingSpecificVersion: true; + allowsListingVersions: true; + }; + } + + export class RemoteSoljson implements Strategy { + allowsLoadingSpecificVersion() { + return true; + } + + allowsListingVersions() { + return true; + } + + async load(_version?: string) { + return { compile: (): any => null }; + } + + async list(): Promise { + return []; + } + } + ``` + +
+

+ 3. Connect everything: - e.g.: +

+

+ See example supplier ```typescript import { Supplier, forDefinition } from "@truffle/supplier"; @@ -93,3 +153,6 @@ specific solc versions from the web / via docker / et al.) export const createCompilerSupplier = forDefinition(definition); ``` + +
+