aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts-gen
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts-gen')
-rw-r--r--packages/contracts-gen/.npmignore6
-rw-r--r--packages/contracts-gen/CHANGELOG.json1
-rw-r--r--packages/contracts-gen/README.md76
-rwxr-xr-xpackages/contracts-gen/bin/contracts-gen.js2
-rw-r--r--packages/contracts-gen/package.json49
-rw-r--r--packages/contracts-gen/src/contracts-gen.ts175
-rw-r--r--packages/contracts-gen/src/index.ts6
-rw-r--r--packages/contracts-gen/tsconfig.json8
-rw-r--r--packages/contracts-gen/tslint.json3
9 files changed, 326 insertions, 0 deletions
diff --git a/packages/contracts-gen/.npmignore b/packages/contracts-gen/.npmignore
new file mode 100644
index 000000000..d645458f6
--- /dev/null
+++ b/packages/contracts-gen/.npmignore
@@ -0,0 +1,6 @@
+.*
+yarn-error.log
+/src/
+/scripts/
+tsconfig.json
+/lib/monorepo_scripts/
diff --git a/packages/contracts-gen/CHANGELOG.json b/packages/contracts-gen/CHANGELOG.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/packages/contracts-gen/CHANGELOG.json
@@ -0,0 +1 @@
+[]
diff --git a/packages/contracts-gen/README.md b/packages/contracts-gen/README.md
new file mode 100644
index 000000000..feaf9e65f
--- /dev/null
+++ b/packages/contracts-gen/README.md
@@ -0,0 +1,76 @@
+# Contracts Gen
+
+This package allows you to generate boilerplate TypeScript code and configs for smart contracts packages.
+
+## Installation
+
+`yarn add -g @0x/contracts-gen`
+
+## Usage
+
+Run it from within your smart contracts packages.
+
+```bash
+contracts-gen
+```
+
+You should run this tool after each time you move your contracts around to regenerate boilerplate code and configs.
+
+## What can it generate
+
+This tool does the following:
+
+- Reads your `compiler.json`. Specifically the list of smart contracts.
+- Creates `wrapper.ts` file which exports all contract wrappers.
+- Creates `artifacts.ts` file which exports all contract artifacts.
+- Generates list of JSON artifact files in `tsconfig.json`
+- Generates a glob for abi-gen in `package.json`
+
+On top of that - if your `compiler.json` has contracts referenced just by name - it will resolve the name to relative path and put it there.
+It also sorts all the lists in it's output leading to smaller and cleaner diffs.
+
+## Contributing
+
+We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-gen yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-gen yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
diff --git a/packages/contracts-gen/bin/contracts-gen.js b/packages/contracts-gen/bin/contracts-gen.js
new file mode 100755
index 000000000..ec6ab4db6
--- /dev/null
+++ b/packages/contracts-gen/bin/contracts-gen.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+require('../lib/src/contracts-gen.js');
diff --git a/packages/contracts-gen/package.json b/packages/contracts-gen/package.json
new file mode 100644
index 000000000..1265cac69
--- /dev/null
+++ b/packages/contracts-gen/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "@0x/contracts-gen",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Generates boilerplate code for smart contracts packages",
+ "main": "lib/src/index.js",
+ "types": "lib/src/index.d.ts",
+ "scripts": {
+ "lint": "tslint --format stylish --project .",
+ "clean": "shx rm -rf lib",
+ "build": "tsc -b",
+ "build:ci": "yarn build"
+ },
+ "bin": {
+ "contracts-gen": "bin/contracts-gen.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts-gen/README.md",
+ "dependencies": {
+ "@0x/types": "^2.0.1",
+ "@0x/utils": "^4.0.2",
+ "@0x/typescript-typings": "^4.0.0",
+ "ethereum-types": "^2.0.0",
+ "@0x/sol-resolver": "^2.0.1",
+ "lodash": "^4.17.11",
+ "prettier": "^1.16.3",
+ "to-snake-case": "^1.0.0"
+ },
+ "devDependencies": {
+ "@0x/tslint-config": "^3.0.0",
+ "@types/node": "*",
+ "@types/prettier": "^1.15.2",
+ "shx": "^0.2.2",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/contracts-gen/src/contracts-gen.ts b/packages/contracts-gen/src/contracts-gen.ts
new file mode 100644
index 000000000..0160a8204
--- /dev/null
+++ b/packages/contracts-gen/src/contracts-gen.ts
@@ -0,0 +1,175 @@
+#!/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;
+}
diff --git a/packages/contracts-gen/src/index.ts b/packages/contracts-gen/src/index.ts
new file mode 100644
index 000000000..c5f4b01f1
--- /dev/null
+++ b/packages/contracts-gen/src/index.ts
@@ -0,0 +1,6 @@
+/**
+ * This module is a CLI tool. As soon as you run it - it starts doing stuff.
+ * At the same time - our installation tests assume that you can import package without causing side effects.
+ * That's why our main entry point it empty. No side effects. But our secondary entry point - contracts-gen.ts is a CLI tool and starts running as soon as you import/run it.
+ */
+export {};
diff --git a/packages/contracts-gen/tsconfig.json b/packages/contracts-gen/tsconfig.json
new file mode 100644
index 000000000..233008d61
--- /dev/null
+++ b/packages/contracts-gen/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": "."
+ },
+ "include": ["./src/**/*"]
+}
diff --git a/packages/contracts-gen/tslint.json b/packages/contracts-gen/tslint.json
new file mode 100644
index 000000000..dd9053357
--- /dev/null
+++ b/packages/contracts-gen/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["@0x/tslint-config"]
+}