Skip to content

Commit

Permalink
Added support for standardised import resolution (#72)
Browse files Browse the repository at this point in the history
* Added resolver-engine functionality
  • Loading branch information
ritave authored and marekkirejczyk committed Feb 14, 2019
1 parent b0e9760 commit 8ce62d8
Show file tree
Hide file tree
Showing 15 changed files with 169 additions and 313 deletions.
23 changes: 10 additions & 13 deletions lib/compiler/buildUitls.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
export function buildInputObject(sources: any, remappings?: any) {
import { ImportFile } from '@resolver-engine/imports';

export function buildSourcesObject(files: ImportFile[]): Record<string, { content: string}> {
const result: Record<string, { content: string}> = {};
files.map((file) => result[file.url] = {content: file.source});
return result;
}

export function buildInputObject(files: ImportFile[], remappings?: any) {
const sources = buildSourcesObject(files);
return {
language: 'Solidity',
sources,
Expand All @@ -8,15 +17,3 @@ export function buildInputObject(sources: any, remappings?: any) {
}
};
}

function toFullyQualifiedName(path: string): string {
return path.replace(/\\/g, '/');
}

export function buildSources(inputs: string[], transform: (input: string) => string) {
const sources: Record<string, { urls: string[] }> = {};
for (const input of inputs) {
sources[toFullyQualifiedName(input)] = {urls: [transform(input)]};
}
return sources;
}
25 changes: 4 additions & 21 deletions lib/compiler/compileDocker.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {join} from 'path';
import ImportMappingBuilder from './importMappingBuilder';
import {Config} from '../config/config';
import {execSync} from 'child_process';
import {buildInputObject, buildSources} from './buildUitls';
import {buildInputObject, buildSourcesObject} from './buildUitls';
import { ImportFile } from '@resolver-engine/imports';

const CONTAINER_PATH = '/home/project';
const NPM_PATH = '/home/npm';

export function compileDocker(config: Config) {
return async function compile(sources: string[]) {
return async function compile(sources: ImportFile[]) {
const command = createBuildCommand(config);
const input = JSON.stringify(buildInputJson(sources, config), null, 2);
const input = JSON.stringify(buildInputObject(sources), null, 2);
return JSON.parse(execSync(command, {input}).toString());
};
}
Expand All @@ -28,20 +28,3 @@ export function getVolumes(config: Config) {
const hostNpmPath = join(hostPath, config.npmPath);
return `-v ${hostPath}:${CONTAINER_PATH} -v ${hostNpmPath}:${NPM_PATH}`;
}

export function buildInputJson(sources: string[], config: Config) {
return buildInputObject(
buildSources(sources, (input) => join(CONTAINER_PATH, input)),
getMappings(sources, config)
);
}

function getMappings(sources: string[], config: Config) {
const mappingBuilder = new ImportMappingBuilder(
config.sourcesPath,
config.npmPath,
CONTAINER_PATH,
NPM_PATH
);
return mappingBuilder.getMappings(sources);
}
20 changes: 4 additions & 16 deletions lib/compiler/compileNative.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {join, resolve} from 'path';
import {execSync} from 'child_process';
import {Config} from '../config/config';
import ImportMappingBuilder from './importMappingBuilder';
import {buildInputObject, buildSources} from './buildUitls';
import {buildInputObject, buildSourcesObject} from './buildUitls';
import { ImportFile } from '@resolver-engine/imports';

export function compileNative(config: Config) {
return async function compile(sources: string[]) {
return async function compile(sources: ImportFile[]) {
const command = createBuildCommand(config);
const input = JSON.stringify(buildInputJson(sources, config), null, 2);
const input = JSON.stringify(buildInputObject(sources), null, 2);
return JSON.parse(execSync(command, {input}).toString());
};
}
Expand All @@ -19,15 +19,3 @@ export function createBuildCommand(config: Config) {
const allowedPaths = [resolve(config.sourcesPath), resolve(config.npmPath), ...customAllowedPaths];
return `${command} ${params} --allow-paths ${allowedPaths.join(',')}`;
}

function buildInputJson(sources: string[], config: Config) {
return buildInputObject(
buildSources(sources, (input) => join(process.cwd(), input)),
getMappings(sources, config)
);
}

function getMappings(sources: string[], config: Config) {
const mappingBuilder = new ImportMappingBuilder(config.sourcesPath, config.npmPath);
return mappingBuilder.getMappings(sources);
}
14 changes: 4 additions & 10 deletions lib/compiler/compileSolcjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import solc from 'solc';
import {promisify} from 'util';
import {readFileContent} from '../utils';
import {Config} from '../config/config';
import {buildInputObject} from './buildUitls';
import {buildInputObject, buildSourcesObject} from './buildUitls';
import { ImportFile } from '@resolver-engine/imports';

const loadRemoteVersion = promisify(solc.loadRemoteVersion);

Expand All @@ -15,21 +16,14 @@ async function loadCompiler(config: Config) {
}

export function compileSolcjs(config: Config) {
return async function compile(sources: string[], findImports: (file: string) => any) {
return async function compile(sources: ImportFile[], findImports: (file: string) => any) {
const solc = await loadCompiler(config);
const inputs = findInputs(sources);
const input = buildInputObject(convertInputs(inputs));
const input = buildInputObject(sources);
const output = solc.compile(JSON.stringify(input), findImports);
return JSON.parse(output);
};
}

function convertInputs(inputs: Record<string, any>) {
const converted: Record<string, { content: string }> = {};
Object.keys(inputs).map((key) => converted[key.replace(/\\/g, '/')] = {content: inputs[key]});
return converted;
}

export function findInputs(files: string[]) {
return Object.assign({}, ...files.map((file) => ({
[file]: readFileContent(file)
Expand Down
13 changes: 11 additions & 2 deletions lib/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {findInputs} from './findInputs';
import {findImports} from './findImports';
import {loadConfig} from '../config/loadConfig';
import {saveOutput} from './saveOutput';
import {ImportsFsEngine, resolvers} from '@resolver-engine/imports-fs';
import {gatherSources} from '@resolver-engine/imports';

export async function compileProject(configPath: string) {
await compileAndSave(loadConfig(configPath));
Expand All @@ -16,10 +18,17 @@ export async function compileAndSave(config: Config) {
}

export async function compile(config: Config) {
return getCompileFunction(config)(
// Added support for backwards compatibillity - renamable node_modules path
const resolver = ImportsFsEngine().addResolver(
resolvers.BacktrackFsResolver(config.npmPath)
);
const sources = await gatherSources(
findInputs(config.sourcesPath),
findImports(config.npmPath)
'.',
resolver
);

return getCompileFunction(config)(sources, findImports(sources));
}

async function processOutput(output: any, config: Config) {
Expand Down
21 changes: 6 additions & 15 deletions lib/compiler/findImports.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import fs from 'fs';
import path from 'path';
import {readFileContent} from '../utils';
import { ImportFile } from '@resolver-engine/imports';

export function findImports(libraryPath: string) {
export function findImports(sources: ImportFile[]) {
return (file: string) => {
try {
const libFile = path.join(libraryPath, file);
if (fs.existsSync(file)) {
return { contents: readFileContent(file) };
} else if (fs.existsSync(libFile)) {
return { contents: readFileContent(libFile) };
} else {
throw new Error(`File not found: ${file}`);
}
} catch (e) {
return { error: e.message };
const result = sources.find((importFile) => importFile.url === file);
if (result) {
return {contents: result.source};
}
return {error: `File not found: ${file}`};
};
}
3 changes: 2 additions & 1 deletion lib/compiler/getCompileFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Config } from '../config/config';
import {compileSolcjs} from './compileSolcjs';
import {compileNative} from './compileNative';
import {compileDocker} from './compileDocker';
import { ImportFile } from '@resolver-engine/imports';

export type CompileFunction = (
sources: string[],
sources: ImportFile[],
findImports: (file: string) => any
) => any;

Expand Down
75 changes: 0 additions & 75 deletions lib/compiler/importMappingBuilder.ts

This file was deleted.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"node": ">=10.0"
},
"dependencies": {
"@resolver-engine/imports-fs": "^0.3.2",
"@resolver-engine/imports": "^0.3.2",
"ethers": "^4.0.0",
"ganache-core": "2.2.1",
"solc": "^0.5.1"
Expand Down
7 changes: 5 additions & 2 deletions test/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import sinonChai from 'sinon-chai';
import chaiString from 'chai-string';
import {compile, compileAndSave} from '../../lib/compiler/compiler';
import defaultConfig from '../../lib/config/config';
import * as path from 'path';

const sourcesPath = './test/projects/example';
const targetPath = './test/compiler/build';
Expand All @@ -15,7 +16,9 @@ chai.use(sinonChai);
describe('INTEGRATION: Compiler', () => {
it('compile just compiles', async () => {
const output = await compile(config);
const basicTokenOutput = output.contracts['test/projects/example/BasicToken.sol'].BasicToken;
const basicTokenOutput = output.contracts[
'test/projects/example/BasicToken.sol'
].BasicToken;
expect(output.errors).to.equal(undefined);
expect(basicTokenOutput.evm.bytecode.object).to.startsWith('6080604052');
expect(JSON.stringify(basicTokenOutput.abi)).to.startsWith('[{"constant":true,');
Expand All @@ -25,7 +28,7 @@ describe('INTEGRATION: Compiler', () => {
const sourcesPath = './test/projects/invalidContracts'; // tslint:disable-line
const targetPath = './test/projects/build'; // tslint:disable-line
const config = {...defaultConfig, sourcesPath, targetPath}; // tslint:disable-line
const expectedOutput = 'test/projects/invalidContracts/invalid.sol:6:14: ' +
const expectedOutput = `${'test/projects/invalidContracts/invalid.sol'}:6:14: ` +
'DeclarationError: Identifier not found or not unique.\n' +
' function f(wrongType arg) public {\n ^-------^\n';

Expand Down
40 changes: 20 additions & 20 deletions test/compiler/findImports.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import fs from 'fs-extra';
import {expect} from 'chai';
import {findImports} from '../../lib/compiler/findImports';
import {readFileContent} from '../../lib/utils';
import { ImportFile } from '@resolver-engine/imports';

describe('INTEGRATION: findImports', () => {
before(async () => {
await fs.mkdirp('/tmp/waffle/b');
await fs.writeFile('/tmp/waffle/b/b.sol', 'contents of b.sol');
});

after(async () => {
await fs.remove('/tmp/waffle');
});
const DATA: ImportFile[] = [
{
url: '/tmp/waffle/b',
source: 'content of b.sol',
provider: 'unknown'
},
{
url: 'test/projects/example/BasicToken.sol',
source: readFileContent('test/projects/example/BasicToken.sol'),
provider: 'unkown'
}
];

it('finds imports in source path', async () => {
const contents = await readFileContent('test/projects/example/BasicToken.sol');
const result = findImports('/')('test/projects/example/BasicToken.sol');
expect(result).to.deep.equal({contents});
});

it('finds imports in library path', () => {
const result = findImports('/tmp/waffle')('b/b.sol');
expect(result).to.deep.equal({contents: 'contents of b.sol'});
describe('INTEGRATION: findImports', () => {
DATA.forEach((importFile) => {
it(`correctly finds ${importFile.url}`, async () => {
const result = findImports(DATA)(importFile.url);
expect(result).to.be.deep.equal({contents: importFile.source});
});
});

it('findImports file not found', async () => {
const result = findImports('/tmp/waffle')('random/nonexisting.sol');
const result = findImports(DATA)('random/nonexisting.sol');
expect(result).to.deep.equal({error: `File not found: random/nonexisting.sol`});
});
});
Loading

0 comments on commit 8ce62d8

Please sign in to comment.