diff --git a/packages/cli/src/models/dependency/Dependency.ts b/packages/cli/src/models/dependency/Dependency.ts index 48b4e41c6..4b6c17157 100644 --- a/packages/cli/src/models/dependency/Dependency.ts +++ b/packages/cli/src/models/dependency/Dependency.ts @@ -129,11 +129,7 @@ export default class Dependency { public get projectFile(): ProjectFile | never { if (!this._projectFile) { - const filePath = ProjectFile.getExistingFilePath(this.getDependencyFolder()); - if (!filePath) { - throw new Error(`Could not find an .openzeppelin/project.json or zos.json file for '${this.name}'`); - } - this._projectFile = new ProjectFile(filePath); + this._projectFile = ProjectFile.getExistingProject(this.getDependencyFolder()); } return this._projectFile; } diff --git a/packages/cli/src/models/files/ProjectFile.ts b/packages/cli/src/models/files/ProjectFile.ts index 2f675f3f6..43799bb69 100644 --- a/packages/cli/src/models/files/ProjectFile.ts +++ b/packages/cli/src/models/files/ProjectFile.ts @@ -8,7 +8,6 @@ import { Loggy } from '@openzeppelin/upgrades'; import Dependency from '../dependency/Dependency'; import { MANIFEST_VERSION, checkVersion } from './ManifestVersion'; import { OPEN_ZEPPELIN_FOLDER } from './constants'; -import NetworkFile from './NetworkFile'; import { ProjectCompilerOptions } from '../compiler/ProjectCompilerOptions'; interface ConfigFileCompilerOptions { @@ -42,12 +41,29 @@ interface ProjectFileData { telemetryOptIn?: boolean; } +function getProjectPath(file: string, dir: string): [string, string] { + if (!dir) dir = file ? getRootFromFilePath(file) : process.cwd(); + if (!file) file = getFileFromRoot(dir); + return [file, dir]; +} + +function getRootFromFilePath(file: string): string { + const dir = path.dirname(file); + return path.basename(dir) === OPEN_ZEPPELIN_FOLDER ? path.dirname(dir) : dir; +} + +function getFileFromRoot(dir: string): string { + // TODO-v3: remove legacy project file support, preferring the new format over the old one + return [PROJECT_FILE_PATH, LEGACY_PROJECT_FILE_NAME].map(p => path.join(dir, p)).find(fs.existsSync); +} + export const PROJECT_FILE_NAME = 'project.json'; export const PROJECT_FILE_PATH = path.join(OPEN_ZEPPELIN_FOLDER, PROJECT_FILE_NAME); export const LEGACY_PROJECT_FILE_NAME = 'zos.json'; export default class ProjectFile { public filePath: string; + public root: string; public data: ProjectFileData; public static getLinkedDependencies(filePath: string = null): string[] { @@ -56,11 +72,21 @@ export default class ProjectFile { return project.linkedDependencies; } - public constructor(filePath: string = null) { + public static getExistingProject(folder: string): ProjectFile { + const projectFile = new ProjectFile(null, folder); + if (!projectFile.exists()) { + throw new Error(`Could not find an .openzeppelin/project.json or zos.json file in ${folder}`); + } + return projectFile; + } + + public constructor(filePath: string = null, root: string = null) { const defaultData = { manifestVersion: MANIFEST_VERSION, } as any; - this.filePath = filePath ?? ProjectFile.getExistingFilePath(process.cwd()); + + [this.filePath, this.root] = getProjectPath(filePath, root); + if (this.filePath) { try { this.data = fs.existsSync(this.filePath) ? fs.readJsonSync(this.filePath) : null; @@ -83,10 +109,6 @@ export default class ProjectFile { return fs.existsSync(this.filePath); } - public get root(): string { - return path.dirname(this.filePath); - } - public set manifestVersion(version: string) { if (this.data.manifestVersion) { this.data.manifestVersion = version; @@ -199,8 +221,8 @@ export default class ProjectFile { const configOptions: ConfigFileCompilerOptions = { manager, solcVersion: version, - artifactsDir: outputDir, - contractsDir: inputDir, + artifactsDir: outputDir ? path.relative(this.root, outputDir) : undefined, + contractsDir: inputDir ? path.relative(this.root, inputDir) : undefined, compilerSettings: { evmVersion, optimizer: { @@ -270,12 +292,6 @@ export default class ProjectFile { } } - public static getExistingFilePath(dir: string = process.cwd(), ...paths: string[]): string { - // TODO-v3: remove legacy project file support - // Prefer the new format over the old one - return [...paths, `${dir}/${PROJECT_FILE_PATH}`, `${dir}/${LEGACY_PROJECT_FILE_NAME}`].find(fs.existsSync); - } - private hasChanged(): boolean { const currentPackgeFile = fs.existsSync(this.filePath) ? fs.readJsonSync(this.filePath) : null; return !isEqual(this.data, currentPackgeFile); diff --git a/packages/cli/test/models/ProjectFile.test.js b/packages/cli/test/models/ProjectFile.test.js index 0c5abfdbd..5d1bde1ff 100644 --- a/packages/cli/test/models/ProjectFile.test.js +++ b/packages/cli/test/models/ProjectFile.test.js @@ -2,7 +2,7 @@ require('../setup'); import { expect } from 'chai'; - +import path from 'path'; import ProjectFile from '../../src/models/files/ProjectFile'; import { MANIFEST_VERSION } from '../../src/models/files/ManifestVersion'; @@ -38,6 +38,21 @@ describe('ProjectFile', function() { }); }); + describe('instance methods', function() { + describe('#setCompilerOptions', function() { + it('ensures relative paths to project file', function() { + const file = new ProjectFile('test/mocks/packages/new.zos.json'); + file.setCompilerOptions({ + inputDir: path.resolve('test/mocks/myContracts'), // path.resolve ensures absolute paths + outputDir: path.resolve('test/mocks/myBuild/contracts'), + }); + + file.compilerOptions.inputDir.should.eq('../myContracts'); + file.compilerOptions.outputDir.should.eq('../myBuild/contracts'); + }); + }); + }); + it('supports deprecated manifest version', function() { const file = new ProjectFile('test/mocks/packages/package-deprecated-manifest-version.zos.json'); file.data.zosversion.should.eq('2.2');