176 lines
7.4 KiB
JavaScript
176 lines
7.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { NameResolver } from '@0x/sol-resolver';
|
|
import { PackageJSON } from '@0x/types';
|
|
import { logUtils } from '@0x/utils';
|
|
import { CompilerOptions } from 'ethereum-types';
|
|
import * as fs from 'fs';
|
|
import * as _ from 'lodash';
|
|
import * as path from 'path';
|
|
import * as prettier from 'prettier';
|
|
import toSnakeCase = require('to-snake-case');
|
|
|
|
const SOLIDITY_EXTENSION = '.sol';
|
|
const DEFAULT_ARTIFACTS_DIR = 'artifacts';
|
|
const DEFAULT_CONTRACTS_DIR = 'contracts';
|
|
const DEFAULT_WRAPPERS_DIR = 'generated-wrappers';
|
|
const AUTO_GENERATED_BANNER = `/*
|
|
* -----------------------------------------------------------------------------
|
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
|
* -----------------------------------------------------------------------------
|
|
*/`;
|
|
const AUTO_GENERATED_BANNER_FOR_LISTS = `This list is auto-generated by contracts-gen. Don't edit manually.`;
|
|
|
|
(async () => {
|
|
const packageDir = process.cwd();
|
|
const compilerJSON = readJSONFile<CompilerOptions>('compiler.json');
|
|
const contracts = compilerJSON.contracts;
|
|
const contractsDir = compilerJSON.contractsDir || DEFAULT_CONTRACTS_DIR;
|
|
const artifactsDir = compilerJSON.artifactsDir || DEFAULT_ARTIFACTS_DIR;
|
|
const wrappersDir = DEFAULT_WRAPPERS_DIR;
|
|
if (!_.isArray(contracts)) {
|
|
throw new Error('Unable to run the generator bacause contracts key in compiler.json is not of type array');
|
|
}
|
|
const prettierConfig = await prettier.resolveConfig(packageDir);
|
|
generateCompilerJSONContractsList(contracts, contractsDir, prettierConfig);
|
|
generateArtifactsTs(contracts, artifactsDir, prettierConfig);
|
|
generateWrappersTs(contracts, wrappersDir, prettierConfig);
|
|
generateTsConfigJSONFilesList(contracts, artifactsDir, prettierConfig);
|
|
generatePackageJSONABIConfig(contracts, artifactsDir, prettierConfig);
|
|
process.exit(0);
|
|
})().catch(err => {
|
|
logUtils.log(err);
|
|
process.exit(1);
|
|
});
|
|
|
|
function generateCompilerJSONContractsList(
|
|
contracts: string[],
|
|
contractsDir: string,
|
|
prettierConfig: prettier.Options | null,
|
|
): void {
|
|
const COMPILER_JSON_FILE_PATH = 'compiler.json';
|
|
const compilerJSON = readJSONFile<CompilerOptions>(COMPILER_JSON_FILE_PATH);
|
|
compilerJSON.contracts = _.map(contracts, contract => {
|
|
if (contract.endsWith(SOLIDITY_EXTENSION)) {
|
|
// If it's already a relative path - NO-OP.
|
|
return contract;
|
|
} else {
|
|
// If it's just a contract name - resolve it and rewrite.
|
|
return new NameResolver(contractsDir).resolve(contract).path;
|
|
}
|
|
});
|
|
compilerJSON.contracts = _.sortBy(compilerJSON.contracts);
|
|
const compilerJSONString = JSON.stringify(compilerJSON);
|
|
const formattedCompilerJSON = prettier.format(compilerJSONString, {
|
|
...prettierConfig,
|
|
filepath: COMPILER_JSON_FILE_PATH,
|
|
});
|
|
fs.writeFileSync(COMPILER_JSON_FILE_PATH, formattedCompilerJSON);
|
|
}
|
|
|
|
function generateArtifactsTs(contracts: string[], artifactsDir: string, prettierConfig: prettier.Options | null): void {
|
|
const imports = _.map(contracts, contract => {
|
|
const contractName = path.basename(contract, SOLIDITY_EXTENSION);
|
|
const importPath = path.join('..', artifactsDir, `${contractName}.json`);
|
|
return `import * as ${contractName} from '${importPath}';`;
|
|
});
|
|
const sortedImports = _.sortBy(imports);
|
|
const artifacts = _.map(contracts, contract => {
|
|
const contractName = path.basename(contract, SOLIDITY_EXTENSION);
|
|
if (contractName === 'ZRXToken') {
|
|
// HACK(albrow): "as any" hack still required here because ZRXToken does not
|
|
// conform to the v2 artifact type.
|
|
return `${contractName}: (${contractName} as any) as ContractArtifact,`;
|
|
} else {
|
|
return `${contractName}: ${contractName} as ContractArtifact,`;
|
|
}
|
|
});
|
|
const artifactsTs = `
|
|
${AUTO_GENERATED_BANNER}
|
|
import { ContractArtifact } from 'ethereum-types';
|
|
|
|
${sortedImports.join('\n')}
|
|
export const artifacts = {${artifacts.join('\n')}};
|
|
`;
|
|
const ARTIFACTS_TS_FILE_PATH = 'src/artifacts.ts';
|
|
const formattedArtifactsTs = prettier.format(artifactsTs, { ...prettierConfig, filepath: ARTIFACTS_TS_FILE_PATH });
|
|
fs.writeFileSync(ARTIFACTS_TS_FILE_PATH, formattedArtifactsTs);
|
|
}
|
|
|
|
function generateWrappersTs(contracts: string[], wrappersDir: string, prettierConfig: prettier.Options | null): void {
|
|
const imports = _.map(contracts, contract => {
|
|
const contractName = path.basename(contract, SOLIDITY_EXTENSION);
|
|
const outputFileName = makeOutputFileName(contractName);
|
|
const exportPath = path.join('..', wrappersDir, outputFileName);
|
|
return `export * from '${exportPath}';`;
|
|
});
|
|
const sortedImports = _.sortBy(imports);
|
|
const wrappersTs = `
|
|
${AUTO_GENERATED_BANNER}
|
|
${sortedImports.join('\n')}
|
|
`;
|
|
const WRAPPERS_TS_FILE_PATH = 'src/wrappers.ts';
|
|
const formattedArtifactsTs = prettier.format(wrappersTs, { ...prettierConfig, filepath: WRAPPERS_TS_FILE_PATH });
|
|
fs.writeFileSync(WRAPPERS_TS_FILE_PATH, formattedArtifactsTs);
|
|
}
|
|
|
|
function generateTsConfigJSONFilesList(
|
|
contracts: string[],
|
|
artifactsDir: string,
|
|
prettierConfig: prettier.Options | null,
|
|
): void {
|
|
const TS_CONFIG_FILE_PATH = 'tsconfig.json';
|
|
const tsConfig = readJSONFile<any>(TS_CONFIG_FILE_PATH);
|
|
tsConfig.files = _.map(contracts, contract => {
|
|
const contractName = path.basename(contract, SOLIDITY_EXTENSION);
|
|
const artifactPath = path.join(artifactsDir, `${contractName}.json`);
|
|
return artifactPath;
|
|
});
|
|
tsConfig.files = _.sortBy(tsConfig.files);
|
|
const tsConfigString = JSON.stringify(tsConfig);
|
|
const formattedTsConfig = prettier.format(tsConfigString, { ...prettierConfig, filepath: TS_CONFIG_FILE_PATH });
|
|
fs.writeFileSync(TS_CONFIG_FILE_PATH, formattedTsConfig);
|
|
}
|
|
|
|
function generatePackageJSONABIConfig(
|
|
contracts: string[],
|
|
artifactsDir: string,
|
|
prettierConfig: prettier.Options | null,
|
|
): void {
|
|
let packageJSON = readJSONFile<PackageJSON>('package.json');
|
|
const contractNames = _.map(contracts, contract => {
|
|
const contractName = path.basename(contract, SOLIDITY_EXTENSION);
|
|
return contractName;
|
|
});
|
|
const sortedContractNames = _.sortBy(contractNames);
|
|
packageJSON = {
|
|
...packageJSON,
|
|
config: {
|
|
...packageJSON.config,
|
|
'abis:comment': AUTO_GENERATED_BANNER_FOR_LISTS,
|
|
abis: `${artifactsDir}/@(${sortedContractNames.join('|')}).json`,
|
|
},
|
|
};
|
|
const PACKAGE_JSON_FILE_PATH = 'package.json';
|
|
const packageJSONString = JSON.stringify(packageJSON);
|
|
const formattedPackageJSON = prettier.format(packageJSONString, {
|
|
...prettierConfig,
|
|
filepath: PACKAGE_JSON_FILE_PATH,
|
|
});
|
|
fs.writeFileSync(PACKAGE_JSON_FILE_PATH, formattedPackageJSON);
|
|
}
|
|
|
|
function makeOutputFileName(name: string): string {
|
|
let fileName = toSnakeCase(name);
|
|
// HACK: Snake case doesn't make a lot of sense for abbreviated names but we can't reliably detect abbreviations
|
|
// so we special-case the abbreviations we use.
|
|
fileName = fileName.replace('z_r_x', 'zrx').replace('e_r_c', 'erc');
|
|
return fileName;
|
|
}
|
|
|
|
function readJSONFile<T>(filePath: string): T {
|
|
const JSONString = fs.readFileSync(filePath, 'utf8');
|
|
const parsed: T = JSON.parse(JSONString);
|
|
return parsed;
|
|
}
|