diff --git a/package-lock.json b/package-lock.json index 00adb59..0d4f3bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "glob": "^8.0.1", + "lodash": "^4.17.21", "semver": "^7.3.7" }, "devDependencies": { @@ -17,6 +18,7 @@ "@types/debug": "^4.1.7", "@types/fs-extra": "^5.0.4", "@types/glob": "^7.2.0", + "@types/lodash": "^4.14.182", "@types/mocha": "^5.2.6", "@types/node": "^8.10.38", "@types/semver": "^7.3.9", @@ -1268,6 +1270,12 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.14.190", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.190.tgz", + "integrity": "sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw==", + "dev": true + }, "node_modules/@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3787,8 +3795,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/log-symbols": { "version": "3.0.0", @@ -6537,6 +6544,12 @@ "@types/node": "*" } }, + "@types/lodash": { + "version": "4.14.190", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.190.tgz", + "integrity": "sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw==", + "dev": true + }, "@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8428,8 +8441,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "log-symbols": { "version": "3.0.0", diff --git a/package.json b/package.json index 0eb3950..89fea1d 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@types/debug": "^4.1.7", "@types/fs-extra": "^5.0.4", "@types/glob": "^7.2.0", + "@types/lodash": "^4.14.182", "@types/mocha": "^5.2.6", "@types/node": "^8.10.38", "@types/semver": "^7.3.9", @@ -54,6 +55,7 @@ }, "dependencies": { "glob": "^8.0.1", + "lodash": "^4.17.21", "semver": "^7.3.7" }, "bugs": { diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 0000000..ba86664 --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,131 @@ +import type { LoDashStatic } from "lodash"; +import type { ProjectPathsConfig } from "hardhat/types/config"; +import type { FeConfig } from "./types"; + +import path from "path"; +import fsExtra from "fs-extra"; +import * as t from "io-ts"; + +import { CACHE_FORMAT_VERSION, FE_FILES_CACHE_FILENAME } from "./constants"; +import { getLogger } from "./util"; + +const log = getLogger("cache"); + +const CacheEntryCodec = t.type({ + lastModificationDate: t.number, + contentHash: t.string, + sourceName: t.string, + feConfig: t.any, + versionPragma: t.string, + artifacts: t.array(t.string), +}); + +const CacheCodec = t.type({ + _format: t.string, + files: t.record(t.string, CacheEntryCodec), +}); + +export interface CacheEntry { + lastModificationDate: number; + contentHash: string; + sourceName: string; + feConfig: FeConfig; + versionPragma: string; + artifacts: string[]; +} + +export interface Cache { + _format: string; + files: Record; +} + +export class FeFilesCache { + constructor(private _cache: Cache) { } + + public static createEmpty(): FeFilesCache { + return new FeFilesCache({ _format: CACHE_FORMAT_VERSION, files: {} }); + } + + public static async readFromFile( + feFilesCachePath: string + ): Promise { + const cacheRaw: Cache = fsExtra.existsSync(feFilesCachePath) + ? fsExtra.readJSONSync(feFilesCachePath) + : { + _format: CACHE_FORMAT_VERSION, + files: {}, + }; + + const result = CacheCodec.decode(cacheRaw); + + if (result.isRight()) { + const feFilesCache = new FeFilesCache(result.value); + await feFilesCache.removeNonExistingFiles(); + return feFilesCache; + } + + log("There was a problem reading the cache"); + + return FeFilesCache.createEmpty(); + } + + public async removeNonExistingFiles() { + for (const absolutePath of Object.keys(this._cache.files)) { + if (!fsExtra.existsSync(absolutePath)) { + this.removeEntry(absolutePath); + } + } + } + + public async writeToFile(feFilesCachePath: string) { + await fsExtra.outputJson(feFilesCachePath, this._cache, { spaces: 2 }); + } + + public addFile(absolutePath: string, entry: CacheEntry) { + this._cache.files[absolutePath] = entry; + } + + public getEntries(): CacheEntry[] { + return Object.values(this._cache.files); + } + + public getEntry(file: string): CacheEntry | undefined { + return this._cache.files[file]; + } + + public removeEntry(file: string) { + delete this._cache.files[file]; + } + + public hasFileChanged( + absolutePath: string, + contentHash: string, + feConfig?: FeConfig + ): boolean { + const { isEqual }: LoDashStatic = require("lodash"); + + const cacheEntry = this.getEntry(absolutePath); + + if (cacheEntry === undefined) { + // new file or no cache available, assume it's new + return true; + } + + if (cacheEntry.contentHash !== contentHash) { + return true; + } + + if ( + feConfig !== undefined && + !isEqual(feConfig, cacheEntry.feConfig) + ) { + return true; + } + + return false; + } +} + +export function getFeFilesCachePath(paths: ProjectPathsConfig): string { + return path.join(paths.cache, FE_FILES_CACHE_FILENAME); +} diff --git a/src/constants.ts b/src/constants.ts index 5593c37..c9dd570 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -8,3 +8,6 @@ export const DEBUG_NAMESPACE = "hardhat:plugin:fe"; export const GITHUB_RELEASES_URL = "https://api.github.com/repos/ethereum/fe/releases?per_page=100"; + +export const CACHE_FORMAT_VERSION = "hh-fe-cache-1"; +export const FE_FILES_CACHE_FILENAME = "fe-files-cache.json"; diff --git a/src/types.ts b/src/types.ts index c5247b2..a9212a3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,10 @@ export interface FeBuild { compilerPath: string; } +export interface FeConfig { + version: string; +} + export enum CompilerPlatform { LINUX = "amd64", MACOS = "mac",