diff options
28 files changed, 618 insertions, 437 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 96ba28b75..20af430d6 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -83,6 +83,7 @@ }, "dependencies": { "@0xproject/assert": "^0.0.20", + "@0xproject/base-contract": "^0.0.1", "@0xproject/json-schemas": "^0.7.12", "@0xproject/types": "^0.2.3", "@0xproject/utils": "^0.3.4", diff --git a/packages/base-contract/.npmignore b/packages/base-contract/.npmignore new file mode 100644 index 000000000..ad5ffcd56 --- /dev/null +++ b/packages/base-contract/.npmignore @@ -0,0 +1,5 @@ +.* +yarn-error.log +/scripts/ +/src/ +tsconfig.json diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md new file mode 100644 index 000000000..74e983f1c --- /dev/null +++ b/packages/base-contract/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG + +## v0.0.1 - _TBD, 2018_ + + * Initial release (#TBD) diff --git a/packages/base-contract/README.md b/packages/base-contract/README.md new file mode 100644 index 000000000..ff0d4d303 --- /dev/null +++ b/packages/base-contract/README.md @@ -0,0 +1,53 @@ +## @0xproject/base-contract + +BaseContract to derive all auto-generated wrappers from + +## Installation + +```bash +yarn add @0xproject/base-contract +``` + +## Usage + +```javascript +import { BaseContract } from '@0xproject/base-contract'; +``` + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. 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 + +```bash +yarn build +``` + +or + +```bash +yarn build:watch +``` + +### Lint + +```bash +yarn lint +``` diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json new file mode 100644 index 000000000..6edbed161 --- /dev/null +++ b/packages/base-contract/package.json @@ -0,0 +1,39 @@ +{ + "name": "@0xproject/base-contract", + "version": "0.0.1", + "description": "0x Base TS contract", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build:watch": "tsc -w", + "build": "tsc", + "clean": "shx rm -rf lib", + "lint": "tslint --project . 'src/**/*.ts'" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x.js.git" + }, + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/base-contract/README.md", + "devDependencies": { + "@0xproject/tslint-config": "^0.4.9", + "@types/lodash": "^4.14.86", + "npm-run-all": "^4.1.2", + "shx": "^0.2.2", + "tslint": "5.8.0", + "typescript": "2.7.1", + "ethers-typescript-typings": "^0.0.1", + "web3-typescript-typings": "^0.9.11" + }, + "dependencies": { + "@0xproject/types": "^0.2.3", + "@0xproject/web3-wrapper": "^0.1.14", + "ethers-contracts": "^2.2.1", + "lodash": "^4.17.4", + "web3": "^0.20.0" + } +} diff --git a/packages/web3-wrapper/src/base_contract.ts b/packages/base-contract/src/index.ts index 997ce97d3..cc1e16a13 100644 --- a/packages/web3-wrapper/src/base_contract.ts +++ b/packages/base-contract/src/index.ts @@ -1,10 +1,9 @@ import { TxData, TxDataPayable } from '@0xproject/types'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as ethersContracts from 'ethers-contracts'; import * as _ from 'lodash'; import * as Web3 from 'web3'; -import { Web3Wrapper } from './web3_wrapper'; - export class BaseContract { protected _ethersInterface: ethersContracts.Interface; protected _web3Wrapper: Web3Wrapper; diff --git a/packages/base-contract/tsconfig.json b/packages/base-contract/tsconfig.json new file mode 100644 index 000000000..8114d99cd --- /dev/null +++ b/packages/base-contract/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "./src/**/*", + "../../node_modules/web3-typescript-typings/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts" + ] +} diff --git a/packages/base-contract/tslint.json b/packages/base-contract/tslint.json new file mode 100644 index 000000000..ffaefe83a --- /dev/null +++ b/packages/base-contract/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": ["@0xproject/tslint-config"] +} diff --git a/packages/contract_templates/contract.handlebars b/packages/contract_templates/contract.handlebars index b5c2dd9f2..2e8ac3f06 100644 --- a/packages/contract_templates/contract.handlebars +++ b/packages/contract_templates/contract.handlebars @@ -4,9 +4,10 @@ */ // tslint:disable:no-consecutive-blank-lines // tslint:disable-next-line:no-unused-variable +import { BaseContract } from '@0xproject/base-contract'; import { TxData, TxDataPayable } from '@0xproject/types'; import { BigNumber, classUtils, promisify } from '@0xproject/utils'; -import { BaseContract, Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as ethersContracts from 'ethers-contracts'; import * as _ from 'lodash'; import * as Web3 from 'web3'; diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 8159ba244..7cac30069 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -64,6 +64,7 @@ }, "dependencies": { "0x.js": "^0.32.4", + "@0xproject/web3-wrapper": "^0.1.14", "@0xproject/deployer": "^0.1.0", "@0xproject/json-schemas": "^0.7.12", "@0xproject/types": "^0.2.3", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index e827842b0..55f1ffff0 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -26,13 +26,13 @@ "shx": "^0.2.2", "tslint": "5.8.0", "typescript": "2.7.1", - "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11" }, "dependencies": { "@0xproject/types": "^0.2.3", "@0xproject/utils": "^0.3.4", "ethers-contracts": "^2.2.1", + "ethers-typescript-typings": "^0.0.1", "lodash": "^4.17.4", "web3": "^0.20.0" } diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 56dfa227c..a07805344 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -1,2 +1,179 @@ -export { Web3Wrapper } from './web3_wrapper'; -export { BaseContract } from './base_contract'; +import { TransactionReceipt, TxData } from '@0xproject/types'; +import { BigNumber, promisify } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +interface RawLogEntry { + logIndex: string | null; + transactionIndex: string | null; + transactionHash: string; + blockHash: string | null; + blockNumber: string | null; + address: string; + data: string; + topics: string[]; +} + +export class Web3Wrapper { + private _web3: Web3; + private _defaults: Partial<TxData>; + private _jsonRpcRequestId: number; + constructor(provider: Web3.Provider, defaults?: Partial<TxData>) { + if (_.isUndefined((provider as any).sendAsync)) { + // Web3@1.0 provider doesn't support synchronous http requests, + // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x` + // We re-assign the send method so that Web3@1.0 providers work with 0x.js + (provider as any).sendAsync = (provider as any).send; + } + this._web3 = new Web3(); + this._web3.setProvider(provider); + this._defaults = defaults || {}; + this._jsonRpcRequestId = 0; + } + public getContractDefaults(): Partial<TxData> { + return this._defaults; + } + public setProvider(provider: Web3.Provider) { + this._web3.setProvider(provider); + } + public isAddress(address: string): boolean { + return this._web3.isAddress(address); + } + public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> { + const addresses = await this.getAvailableAddressesAsync(); + const normalizedAddress = senderAddress.toLowerCase(); + return _.includes(addresses, normalizedAddress); + } + public async getNodeVersionAsync(): Promise<string> { + const nodeVersion = await promisify<string>(this._web3.version.getNode)(); + return nodeVersion; + } + public async getNetworkIdAsync(): Promise<number> { + const networkIdStr = await promisify<string>(this._web3.version.getNetwork)(); + const networkId = _.parseInt(networkIdStr); + return networkId; + } + public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { + const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash); + if (!_.isNull(transactionReceipt)) { + transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status); + } + return transactionReceipt; + } + public getCurrentProvider(): Web3.Provider { + return this._web3.currentProvider; + } + public toWei(ethAmount: BigNumber): BigNumber { + const balanceWei = this._web3.toWei(ethAmount, 'ether'); + return balanceWei; + } + public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> { + let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner); + // Rewrap in a new BigNumber + balanceInWei = new BigNumber(balanceInWei); + return balanceInWei; + } + public async doesContractExistAtAddressAsync(address: string): Promise<boolean> { + const code = await promisify<string>(this._web3.eth.getCode)(address); + // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients + const codeIsEmpty = /^0x0{0,40}$/i.test(code); + return !codeIsEmpty; + } + public async signTransactionAsync(address: string, message: string): Promise<string> { + const signData = await promisify<string>(this._web3.eth.sign)(address, message); + return signData; + } + public async getBlockNumberAsync(): Promise<number> { + const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)(); + return blockNumber; + } + public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> { + const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam); + return block; + } + public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> { + const { timestamp } = await this.getBlockAsync(blockParam); + return timestamp; + } + public async getAvailableAddressesAsync(): Promise<string[]> { + const addresses = await promisify<string[]>(this._web3.eth.getAccounts)(); + const normalizedAddresses = _.map(addresses, address => address.toLowerCase()); + return normalizedAddresses; + } + public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { + let fromBlock = filter.fromBlock; + if (_.isNumber(fromBlock)) { + fromBlock = this._web3.toHex(fromBlock); + } + let toBlock = filter.toBlock; + if (_.isNumber(toBlock)) { + toBlock = this._web3.toHex(toBlock); + } + const serializedFilter = { + ...filter, + fromBlock, + toBlock, + }; + const payload = { + jsonrpc: '2.0', + id: this._jsonRpcRequestId++, + method: 'eth_getLogs', + params: [serializedFilter], + }; + const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload); + const formattedLogs = _.map(rawLogs, this._formatLog.bind(this)); + return formattedLogs; + } + public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> { + const web3Contract = this._web3.eth.contract(abi); + return web3Contract; + } + public async estimateGasAsync(txData: Partial<Web3.TxData>): Promise<number> { + const gas = await promisify<number>(this._web3.eth.estimateGas)(txData); + return gas; + } + public async callAsync(callData: Web3.CallData, defaultBlock?: Web3.BlockParam): Promise<string> { + const rawCalllResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock); + return rawCalllResult; + } + public async sendTransactionAsync(txData: Web3.TxData): Promise<string> { + const txHash = await promisify<string>(this._web3.eth.sendTransaction)(txData); + return txHash; + } + private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> { + const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider); + const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload); + const result = response.result; + return result; + } + private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 { + // Transaction status might have four values + // undefined - Testrpc and other old clients + // null - New clients on old transactions + // number - Parity + // hex - Geth + if (_.isString(status)) { + return this._web3.toDecimal(status) as 0 | 1; + } else if (_.isUndefined(status)) { + return null; + } else { + return status; + } + } + private _formatLog(rawLog: RawLogEntry): Web3.LogEntry { + const formattedLog = { + ...rawLog, + logIndex: this._hexToDecimal(rawLog.logIndex), + blockNumber: this._hexToDecimal(rawLog.blockNumber), + transactionIndex: this._hexToDecimal(rawLog.transactionIndex), + }; + return formattedLog; + } + private _hexToDecimal(hex: string | null): number | null { + if (_.isNull(hex)) { + return null; + } + const decimal = this._web3.toDecimal(hex); + return decimal; + } +} diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts deleted file mode 100644 index a07805344..000000000 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { TransactionReceipt, TxData } from '@0xproject/types'; -import { BigNumber, promisify } from '@0xproject/utils'; -import * as _ from 'lodash'; -import * as Web3 from 'web3'; - -interface RawLogEntry { - logIndex: string | null; - transactionIndex: string | null; - transactionHash: string; - blockHash: string | null; - blockNumber: string | null; - address: string; - data: string; - topics: string[]; -} - -export class Web3Wrapper { - private _web3: Web3; - private _defaults: Partial<TxData>; - private _jsonRpcRequestId: number; - constructor(provider: Web3.Provider, defaults?: Partial<TxData>) { - if (_.isUndefined((provider as any).sendAsync)) { - // Web3@1.0 provider doesn't support synchronous http requests, - // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x` - // We re-assign the send method so that Web3@1.0 providers work with 0x.js - (provider as any).sendAsync = (provider as any).send; - } - this._web3 = new Web3(); - this._web3.setProvider(provider); - this._defaults = defaults || {}; - this._jsonRpcRequestId = 0; - } - public getContractDefaults(): Partial<TxData> { - return this._defaults; - } - public setProvider(provider: Web3.Provider) { - this._web3.setProvider(provider); - } - public isAddress(address: string): boolean { - return this._web3.isAddress(address); - } - public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> { - const addresses = await this.getAvailableAddressesAsync(); - const normalizedAddress = senderAddress.toLowerCase(); - return _.includes(addresses, normalizedAddress); - } - public async getNodeVersionAsync(): Promise<string> { - const nodeVersion = await promisify<string>(this._web3.version.getNode)(); - return nodeVersion; - } - public async getNetworkIdAsync(): Promise<number> { - const networkIdStr = await promisify<string>(this._web3.version.getNetwork)(); - const networkId = _.parseInt(networkIdStr); - return networkId; - } - public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { - const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash); - if (!_.isNull(transactionReceipt)) { - transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status); - } - return transactionReceipt; - } - public getCurrentProvider(): Web3.Provider { - return this._web3.currentProvider; - } - public toWei(ethAmount: BigNumber): BigNumber { - const balanceWei = this._web3.toWei(ethAmount, 'ether'); - return balanceWei; - } - public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> { - let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner); - // Rewrap in a new BigNumber - balanceInWei = new BigNumber(balanceInWei); - return balanceInWei; - } - public async doesContractExistAtAddressAsync(address: string): Promise<boolean> { - const code = await promisify<string>(this._web3.eth.getCode)(address); - // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients - const codeIsEmpty = /^0x0{0,40}$/i.test(code); - return !codeIsEmpty; - } - public async signTransactionAsync(address: string, message: string): Promise<string> { - const signData = await promisify<string>(this._web3.eth.sign)(address, message); - return signData; - } - public async getBlockNumberAsync(): Promise<number> { - const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)(); - return blockNumber; - } - public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> { - const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam); - return block; - } - public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> { - const { timestamp } = await this.getBlockAsync(blockParam); - return timestamp; - } - public async getAvailableAddressesAsync(): Promise<string[]> { - const addresses = await promisify<string[]>(this._web3.eth.getAccounts)(); - const normalizedAddresses = _.map(addresses, address => address.toLowerCase()); - return normalizedAddresses; - } - public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { - let fromBlock = filter.fromBlock; - if (_.isNumber(fromBlock)) { - fromBlock = this._web3.toHex(fromBlock); - } - let toBlock = filter.toBlock; - if (_.isNumber(toBlock)) { - toBlock = this._web3.toHex(toBlock); - } - const serializedFilter = { - ...filter, - fromBlock, - toBlock, - }; - const payload = { - jsonrpc: '2.0', - id: this._jsonRpcRequestId++, - method: 'eth_getLogs', - params: [serializedFilter], - }; - const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload); - const formattedLogs = _.map(rawLogs, this._formatLog.bind(this)); - return formattedLogs; - } - public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> { - const web3Contract = this._web3.eth.contract(abi); - return web3Contract; - } - public async estimateGasAsync(txData: Partial<Web3.TxData>): Promise<number> { - const gas = await promisify<number>(this._web3.eth.estimateGas)(txData); - return gas; - } - public async callAsync(callData: Web3.CallData, defaultBlock?: Web3.BlockParam): Promise<string> { - const rawCalllResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock); - return rawCalllResult; - } - public async sendTransactionAsync(txData: Web3.TxData): Promise<string> { - const txHash = await promisify<string>(this._web3.eth.sendTransaction)(txData); - return txHash; - } - private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> { - const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider); - const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload); - const result = response.result; - return result; - } - private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 { - // Transaction status might have four values - // undefined - Testrpc and other old clients - // null - New clients on old transactions - // number - Parity - // hex - Geth - if (_.isString(status)) { - return this._web3.toDecimal(status) as 0 | 1; - } else if (_.isUndefined(status)) { - return null; - } else { - return status; - } - } - private _formatLog(rawLog: RawLogEntry): Web3.LogEntry { - const formattedLog = { - ...rawLog, - logIndex: this._hexToDecimal(rawLog.logIndex), - blockNumber: this._hexToDecimal(rawLog.blockNumber), - transactionIndex: this._hexToDecimal(rawLog.transactionIndex), - }; - return formattedLog; - } - private _hexToDecimal(hex: string | null): number | null { - if (_.isNull(hex)) { - return null; - } - const decimal = this._web3.toDecimal(hex); - return decimal; - } -} diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 2723c2218..b2b2d2ebd 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -319,7 +319,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { shouldDisplaySectionHeaders={false} onMenuItemClick={this._onMenuButtonClick.bind(this)} selectedVersion={this.props.docsVersion} - docPath={this.props.docsInfo.websitePath} versions={this.props.availableDocVersions} /> </div> diff --git a/packages/website/ts/containers/connect_documentation.tsx b/packages/website/ts/containers/connect_documentation.tsx index 5c5d26b44..464bdcd74 100644 --- a/packages/website/ts/containers/connect_documentation.tsx +++ b/packages/website/ts/containers/connect_documentation.tsx @@ -2,15 +2,14 @@ import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; -import { Documentation as DocumentationComponent, DocumentationAllProps } from 'ts/pages/documentation/documentation'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocsInfoConfig, Environments, WebsitePaths } from 'ts/types'; +import { DocPackages, DocsInfoConfig, Environments, SupportedDocJson, WebsitePaths } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -import { typeDocUtils } from 'ts/utils/typedoc_utils'; /* tslint:disable:no-var-requires */ const IntroMarkdown = require('md/docs/connect/introduction'); @@ -25,16 +24,11 @@ const connectDocSections = { types: constants.TYPES_SECTION_NAME, }; -const s3BucketName = - configs.ENVIRONMENT === Environments.DEVELOPMENT ? 'staging-connect-docs-jsons' : 'connect-docs-jsons'; -const docsJsonRoot = `https://s3.amazonaws.com/${s3BucketName}`; - const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.Connect, + type: SupportedDocJson.TypeDoc, displayName: '0x Connect', - subPackageName: 'connect', packageUrl: 'https://github.com/0xProject/0x.js', - websitePath: WebsitePaths.Connect, - docsJsonRoot, menu: { introduction: [connectDocSections.introduction], install: [connectDocSections.installation], @@ -77,7 +71,6 @@ const docsInfoConfig: DocsInfoConfig = { menuSubsectionToVersionWhenIntroduced: {}, sections: connectDocSections, visibleConstructors: [connectDocSections.httpClient, connectDocSections.webSocketOrderbookChannel], - convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), }; const docsInfo = new DocsInfo(docsInfoConfig); @@ -92,7 +85,7 @@ interface ConnectedDispatch { dispatcher: Dispatcher; } -const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ +const mapStateToProps = (state: State, ownProps: DocPageProps): ConnectedState => ({ docsVersion: state.docsVersion, availableDocVersions: state.availableDocVersions, translate: state.translate, @@ -103,6 +96,6 @@ const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ dispatcher: new Dispatcher(dispatch), }); -export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, +export const Documentation: React.ComponentClass<DocPageProps> = connect(mapStateToProps, mapDispatchToProps)( + DocPageComponent, ); diff --git a/packages/website/ts/containers/smart_contracts_documentation.tsx b/packages/website/ts/containers/smart_contracts_documentation.tsx index c4731f929..a839529aa 100644 --- a/packages/website/ts/containers/smart_contracts_documentation.tsx +++ b/packages/website/ts/containers/smart_contracts_documentation.tsx @@ -2,12 +2,18 @@ import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; -import { Documentation as DocumentationComponent, DocumentationAllProps } from 'ts/pages/documentation/documentation'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocsInfoConfig, SmartContractDocSections as Sections, WebsitePaths } from 'ts/types'; -import { doxityUtils } from 'ts/utils/doxity_utils'; +import { + DocPackages, + DocsInfoConfig, + Networks, + SmartContractDocSections as Sections, + SupportedDocJson, + WebsitePaths, +} from 'ts/types'; import { Translate } from 'ts/utils/translate'; /* tslint:disable:no-var-requires */ @@ -15,10 +21,10 @@ const IntroMarkdown = require('md/docs/smart_contracts/introduction'); /* tslint:enable:no-var-requires */ const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.SmartContracts, + type: SupportedDocJson.Doxity, displayName: '0x Smart Contracts', packageUrl: 'https://github.com/0xProject/contracts', - websitePath: WebsitePaths.SmartContracts, - docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json', menu: { introduction: [Sections.Introduction], contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], @@ -34,7 +40,34 @@ const docsInfoConfig: DocsInfoConfig = { ZRXToken: Sections.ZRXToken, }, visibleConstructors: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy], - convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils), + contractsByVersionByNetworkId: { + '1.0.0': { + [Networks.Mainnet]: { + [Sections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093', + [Sections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', + [Sections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498', + [Sections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c', + }, + [Networks.Ropsten]: { + [Sections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac', + [Sections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', + [Sections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', + [Sections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed', + }, + [Networks.Kovan]: { + [Sections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364', + [Sections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4', + [Sections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', + [Sections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f', + }, + [Networks.Rinkeby]: { + [Sections.Exchange]: '0x1d16ef40fac01cec8adac2ac49427b9384192c05', + [Sections.TokenTransferProxy]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', + [Sections.ZRXToken]: '0x00f58d6d585f84b2d7267940cede30ce2fe6eae8', + [Sections.TokenRegistry]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', + }, + }, + }, }; const docsInfo = new DocsInfo(docsInfoConfig); @@ -49,7 +82,7 @@ interface ConnectedDispatch { docsInfo: DocsInfo; } -const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ +const mapStateToProps = (state: State, ownProps: DocPageProps): ConnectedState => ({ docsVersion: state.docsVersion, availableDocVersions: state.availableDocVersions, translate: state.translate, @@ -60,6 +93,6 @@ const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ docsInfo, }); -export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, +export const Documentation: React.ComponentClass<DocPageProps> = connect(mapStateToProps, mapDispatchToProps)( + DocPageComponent, ); diff --git a/packages/website/ts/containers/zero_ex_js_documentation.tsx b/packages/website/ts/containers/zero_ex_js_documentation.tsx index a32a87f7f..500bf8d96 100644 --- a/packages/website/ts/containers/zero_ex_js_documentation.tsx +++ b/packages/website/ts/containers/zero_ex_js_documentation.tsx @@ -2,15 +2,14 @@ import * as _ from 'lodash'; import * as React from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { DocPage as DocPageComponent, DocPageProps } from 'ts/pages/documentation/doc_page'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; -import { Documentation as DocumentationComponent, DocumentationAllProps } from 'ts/pages/documentation/documentation'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { DocsInfoConfig, Environments, WebsitePaths } from 'ts/types'; +import { DocPackages, DocsInfoConfig, Environments, SupportedDocJson, WebsitePaths } from 'ts/types'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -import { typeDocUtils } from 'ts/utils/typedoc_utils'; /* tslint:disable:no-var-requires */ const IntroMarkdown = require('md/docs/0xjs/introduction'); @@ -37,15 +36,11 @@ const zeroExJsDocSections = { types: constants.TYPES_SECTION_NAME, }; -const s3BucketName = configs.ENVIRONMENT === Environments.DEVELOPMENT ? 'staging-0xjs-docs-jsons' : '0xjs-docs-jsons'; -const docsJsonRoot = `https://s3.amazonaws.com/${s3BucketName}`; - const docsInfoConfig: DocsInfoConfig = { + id: DocPackages.ZeroExJs, + type: SupportedDocJson.TypeDoc, displayName: '0x.js', packageUrl: 'https://github.com/0xProject/0x.js', - subPackageName: '0x.js', - websitePath: WebsitePaths.ZeroExJs, - docsJsonRoot, menu: { introduction: [zeroExJsDocSections.introduction], install: [zeroExJsDocSections.installation], @@ -69,7 +64,8 @@ const docsInfoConfig: DocsInfoConfig = { [zeroExJsDocSections.versioning]: versioningMarkdown, }, // Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is - // currently no way to extract the re-exported types from index.ts via TypeDoc :( + // currently no way to extract the re-exported types from index.ts via TypeDoc :( Make sure to only + // ADD types here, DO NOT REMOVE types since they might still be needed for older supported versions publicTypes: [ 'Order', 'SignedOrder', @@ -147,7 +143,6 @@ const docsInfoConfig: DocsInfoConfig = { }, sections: zeroExJsDocSections, visibleConstructors: [zeroExJsDocSections.zeroEx], - convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils), }; const docsInfo = new DocsInfo(docsInfoConfig); @@ -162,7 +157,7 @@ interface ConnectedDispatch { dispatcher: Dispatcher; } -const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({ +const mapStateToProps = (state: State, ownProps: DocPageProps): ConnectedState => ({ docsVersion: state.docsVersion, availableDocVersions: state.availableDocVersions, docsInfo, @@ -173,6 +168,6 @@ const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ dispatcher: new Dispatcher(dispatch), }); -export const Documentation: React.ComponentClass<DocumentationAllProps> = connect(mapStateToProps, mapDispatchToProps)( - DocumentationComponent, +export const Documentation: React.ComponentClass<DocPageProps> = connect(mapStateToProps, mapDispatchToProps)( + DocPageComponent, ); diff --git a/packages/website/ts/pages/documentation/doc_page.tsx b/packages/website/ts/pages/documentation/doc_page.tsx new file mode 100644 index 000000000..2c8f1c103 --- /dev/null +++ b/packages/website/ts/pages/documentation/doc_page.tsx @@ -0,0 +1,132 @@ +import findVersions = require('find-versions'); +import * as _ from 'lodash'; +import * as React from 'react'; +import DocumentTitle = require('react-document-title'); +import semverSort = require('semver-sort'); +import { TopBar } from 'ts/components/top_bar/top_bar'; +import { DocsInfo } from 'ts/pages/documentation/docs_info'; +import { Documentation } from 'ts/pages/documentation/documentation'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { DocAgnosticFormat, DocPackages, DoxityDocObj, Environments, MenuSubsectionsBySection } from 'ts/types'; +import { configs } from 'ts/utils/configs'; +import { constants } from 'ts/utils/constants'; +import { docUtils } from 'ts/utils/doc_utils'; +import { Translate } from 'ts/utils/translate'; + +const docIdToS3BucketName: { [id: string]: string } = { + [DocPackages.ZeroExJs]: '0xjs-docs-jsons', + [DocPackages.SmartContracts]: 'smart-contracts-docs-json', + [DocPackages.Connect]: + configs.ENVIRONMENT === Environments.DEVELOPMENT ? 'staging-connect-docs-jsons' : 'connect-docs-jsons', +}; + +const docIdToSubpackageName: { [id: string]: string } = { + [DocPackages.ZeroExJs]: '0x.js', + [DocPackages.Connect]: 'connect', + [DocPackages.SmartContracts]: 'contracts', +}; + +export interface DocPageProps { + location: Location; + dispatcher: Dispatcher; + docsVersion: string; + availableDocVersions: string[]; + docsInfo: DocsInfo; + translate: Translate; +} + +interface DocPageState { + docAgnosticFormat?: DocAgnosticFormat; +} + +export class DocPage extends React.Component<DocPageProps, DocPageState> { + private _isUnmounted: boolean; + constructor(props: DocPageProps) { + super(props); + this._isUnmounted = false; + this.state = { + docAgnosticFormat: undefined, + }; + } + public componentWillMount() { + const pathName = this.props.location.pathname; + const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1); + const versions = findVersions(lastSegment); + const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined; + // tslint:disable-next-line:no-floating-promises + this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists); + } + public componentWillUnmount() { + this._isUnmounted = true; + } + + public render() { + const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) + ? {} + : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); + const sourceUrl = this._getSourceUrl(); + return ( + <div> + <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} /> + <TopBar + blockchainIsLoaded={false} + location={this.props.location} + docsVersion={this.props.docsVersion} + availableDocVersions={this.props.availableDocVersions} + menu={this.props.docsInfo.getMenu(this.props.docsVersion)} + menuSubsectionsBySection={menuSubsectionsBySection} + docsInfo={this.props.docsInfo} + translate={this.props.translate} + /> + <Documentation + location={this.props.location} + docsVersion={this.props.docsVersion} + availableDocVersions={this.props.availableDocVersions} + docsInfo={this.props.docsInfo} + docAgnosticFormat={this.state.docAgnosticFormat} + menuSubsectionsBySection={menuSubsectionsBySection} + sourceUrl={sourceUrl} + /> + </div> + ); + } + private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> { + const s3BucketName = docIdToS3BucketName[this.props.docsInfo.id]; + const docsJsonRoot = `${constants.S3_BUCKET_ROOT}/${s3BucketName}`; + const versionToFileName = await docUtils.getVersionToFileNameAsync(docsJsonRoot); + const versions = _.keys(versionToFileName); + this.props.dispatcher.updateAvailableDocVersions(versions); + const sortedVersions = semverSort.desc(versions); + const latestVersion = sortedVersions[0]; + + let versionToFetch = latestVersion; + if (!_.isUndefined(preferredVersionIfExists)) { + const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; + if (!_.isUndefined(preferredVersionFileNameIfExists)) { + versionToFetch = preferredVersionIfExists; + } + } + this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); + + const versionFileNameToFetch = versionToFileName[versionToFetch]; + const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, docsJsonRoot); + const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj); + + if (!this._isUnmounted) { + this.setState({ + docAgnosticFormat, + }); + } + } + private _getSourceUrl() { + const url = this.props.docsInfo.packageUrl; + const pkg = docIdToSubpackageName[this.props.docsInfo.id]; + let tagPrefix = pkg; + const packagesWithNamespace = ['connect']; + if (_.includes(packagesWithNamespace, pkg)) { + tagPrefix = `@0xproject/${pkg}`; + } + const sourceUrl = `${url}/blob/${tagPrefix}%40${this.props.docsVersion}/packages/${pkg}`; + return sourceUrl; + } +} diff --git a/packages/website/ts/pages/documentation/docs_info.ts b/packages/website/ts/pages/documentation/docs_info.ts index 4b1ec122a..31e151fe8 100644 --- a/packages/website/ts/pages/documentation/docs_info.ts +++ b/packages/website/ts/pages/documentation/docs_info.ts @@ -1,33 +1,37 @@ import compareVersions = require('compare-versions'); import * as _ from 'lodash'; import { + ContractsByVersionByNetworkId, DocAgnosticFormat, DocsInfoConfig, DocsMenu, DoxityDocObj, MenuSubsectionsBySection, SectionsMap, + SupportedDocJson, TypeDocNode, } from 'ts/types'; +import { doxityUtils } from 'ts/utils/doxity_utils'; +import { typeDocUtils } from 'ts/utils/typedoc_utils'; export class DocsInfo { + public id: string; + public type: SupportedDocJson; public displayName: string; public packageUrl: string; - public subPackageName?: string; - public websitePath: string; - public docsJsonRoot: string; public menu: DocsMenu; public sections: SectionsMap; public sectionNameToMarkdown: { [sectionName: string]: string }; + public contractsByVersionByNetworkId?: ContractsByVersionByNetworkId; private _docsInfo: DocsInfoConfig; constructor(config: DocsInfoConfig) { + this.id = config.id; + this.type = config.type; this.displayName = config.displayName; this.packageUrl = config.packageUrl; - this.subPackageName = config.subPackageName; - this.websitePath = config.websitePath; - this.docsJsonRoot = config.docsJsonRoot; this.sections = config.sections; this.sectionNameToMarkdown = config.sectionNameToMarkdown; + this.contractsByVersionByNetworkId = config.contractsByVersionByNetworkId; this._docsInfo = config; } public isPublicType(typeName: string): boolean { @@ -106,6 +110,10 @@ export class DocsInfo { return _.includes(this._docsInfo.visibleConstructors, sectionName); } public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat { - return this._docsInfo.convertToDocAgnosticFormatFn(docObj, this); + if (this.type === SupportedDocJson.Doxity) { + return doxityUtils.convertToDocAgnosticFormat(docObj as DoxityDocObj); + } else { + return typeDocUtils.convertToDocAgnosticFormat(docObj as TypeDocNode, this); + } } } diff --git a/packages/website/ts/pages/documentation/documentation.tsx b/packages/website/ts/pages/documentation/documentation.tsx index 285471166..7eed78fc3 100644 --- a/packages/website/ts/pages/documentation/documentation.tsx +++ b/packages/website/ts/pages/documentation/documentation.tsx @@ -1,11 +1,7 @@ -import findVersions = require('find-versions'); import * as _ from 'lodash'; import CircularProgress from 'material-ui/CircularProgress'; import * as React from 'react'; -import DocumentTitle = require('react-document-title'); import { scroller } from 'react-scroll'; -import semverSort = require('semver-sort'); -import { TopBar } from 'ts/components/top_bar/top_bar'; import { Badge } from 'ts/components/ui/badge'; import { Comment } from 'ts/pages/documentation/comment'; import { DocsInfo } from 'ts/pages/documentation/docs_info'; @@ -17,25 +13,23 @@ import { TypeDefinition } from 'ts/pages/documentation/type_definition'; import { MarkdownSection } from 'ts/pages/shared/markdown_section'; import { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu'; import { SectionHeader } from 'ts/pages/shared/section_header'; -import { Dispatcher } from 'ts/redux/dispatcher'; import { AddressByContractName, DocAgnosticFormat, DoxityDocObj, EtherscanLinkSuffixes, Event, + MenuSubsectionsBySection, Networks, Property, SolidityMethod, Styles, + SupportedDocJson, TypeDefinitionByName, TypescriptMethod, } from 'ts/types'; import { colors } from 'ts/utils/colors'; -import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; -import { docUtils } from 'ts/utils/doc_utils'; -import { Translate } from 'ts/utils/translate'; import { utils } from 'ts/utils/utils'; const TOP_BAR_HEIGHT = 60; @@ -48,20 +42,18 @@ const networkNameToColor: { [network: string]: string } = { [Networks.Rinkeby]: colors.darkYellow, }; -export interface DocumentationAllProps { - source: string; +export interface DocumentationProps { location: Location; - dispatcher: Dispatcher; docsVersion: string; availableDocVersions: string[]; docsInfo: DocsInfo; - translate: Translate; -} - -interface DocumentationState { docAgnosticFormat?: DocAgnosticFormat; + menuSubsectionsBySection: MenuSubsectionsBySection; + sourceUrl: string; } +interface DocumentationState {} + const styles: Styles = { mainContainers: { position: 'absolute', @@ -81,57 +73,17 @@ const styles: Styles = { }, }; -export class Documentation extends React.Component<DocumentationAllProps, DocumentationState> { - private _isUnmounted: boolean; - constructor(props: DocumentationAllProps) { - super(props); - this._isUnmounted = false; - this.state = { - docAgnosticFormat: undefined, - }; - } - public componentWillMount() { - const pathName = this.props.location.pathname; - const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1); - const versions = findVersions(lastSegment); - const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined; - // tslint:disable-next-line:no-floating-promises - this._fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists); - } - public componentWillUnmount() { - this._isUnmounted = true; +export class Documentation extends React.Component<DocumentationProps, DocumentationState> { + public componentDidUpdate(prevProps: DocumentationProps, prevState: DocumentationState) { + if (!_.isEqual(prevProps.docAgnosticFormat, this.props.docAgnosticFormat)) { + this._scrollToHash(); + } } public render() { - const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) - ? {} - : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat); return ( <div> - <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} /> - <TopBar - blockchainIsLoaded={false} - location={this.props.location} - docsVersion={this.props.docsVersion} - availableDocVersions={this.props.availableDocVersions} - menu={this.props.docsInfo.getMenu(this.props.docsVersion)} - menuSubsectionsBySection={menuSubsectionsBySection} - docsInfo={this.props.docsInfo} - translate={this.props.translate} - /> - {_.isUndefined(this.state.docAgnosticFormat) ? ( - <div className="col col-12" style={styles.mainContainers}> - <div - className="relative sm-px2 sm-pt2 sm-m1" - style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }} - > - <div className="center pb2"> - <CircularProgress size={40} thickness={5} /> - </div> - <div className="center pt2" style={{ paddingBottom: 11 }}> - Loading documentation... - </div> - </div> - </div> + {_.isUndefined(this.props.docAgnosticFormat) ? ( + this._renderLoading() ) : ( <div style={{ width: '100%', height: '100%', backgroundColor: colors.gray40 }}> <div @@ -155,8 +107,7 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume versions={this.props.availableDocVersions} title={this.props.docsInfo.displayName} topLevelMenu={this.props.docsInfo.getMenu(this.props.docsVersion)} - menuSubsectionsBySection={menuSubsectionsBySection} - docPath={this.props.docsInfo.websitePath} + menuSubsectionsBySection={this.props.menuSubsectionsBySection} /> </div> </div> @@ -175,11 +126,28 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume </div> ); } + private _renderLoading() { + return ( + <div className="col col-12" style={styles.mainContainers}> + <div + className="relative sm-px2 sm-pt2 sm-m1" + style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }} + > + <div className="center pb2"> + <CircularProgress size={40} thickness={5} /> + </div> + <div className="center pt2" style={{ paddingBottom: 11 }}> + Loading documentation... + </div> + </div> + </div> + ); + } private _renderDocumentation(): React.ReactNode { const subMenus = _.values(this.props.docsInfo.getMenu()); const orderedSectionNames = _.flatten(subMenus); - const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat); + const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.props.docAgnosticFormat); const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName)); return renderedSections; @@ -196,7 +164,7 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume ); } - const docSection = this.state.docAgnosticFormat[sectionName]; + const docSection = this.props.docAgnosticFormat[sectionName]; if (_.isUndefined(docSection)) { return null; } @@ -278,7 +246,13 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume ); } private _renderNetworkBadgesIfExists(sectionName: string) { - const networkToAddressByContractName = configs.CONTRACT_ADDRESS[this.props.docsVersion]; + if (this.props.docsInfo.type !== SupportedDocJson.Doxity) { + return null; + } + + const networkToAddressByContractName = this.props.docsInfo.contractsByVersionByNetworkId[ + this.props.docsVersion + ]; const badges = _.map( networkToAddressByContractName, (addressByContractName: AddressByContractName, networkName: string) => { @@ -326,8 +300,7 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume <SourceLink version={this.props.docsVersion} source={property.source} - baseUrl={this.props.docsInfo.packageUrl} - subPackageName={this.props.docsInfo.subPackageName} + sourceUrl={this.props.sourceUrl} /> )} {property.comment && <Comment comment={property.comment} className="py2" />} @@ -348,6 +321,7 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume typeDefinitionByName={typeDefinitionByName} libraryVersion={this.props.docsVersion} docsInfo={this.props.docsInfo} + sourceUrl={this.props.sourceUrl} /> ); } @@ -364,38 +338,4 @@ export class Documentation extends React.Component<DocumentationAllProps, Docume containerId: 'documentation', }); } - private async _fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> { - const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot); - const versions = _.keys(versionToFileName); - this.props.dispatcher.updateAvailableDocVersions(versions); - const sortedVersions = semverSort.desc(versions); - const latestVersion = sortedVersions[0]; - - let versionToFetch = latestVersion; - if (!_.isUndefined(preferredVersionIfExists)) { - const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists]; - if (!_.isUndefined(preferredVersionFileNameIfExists)) { - versionToFetch = preferredVersionIfExists; - } - } - this.props.dispatcher.updateCurrentDocsVersion(versionToFetch); - - const versionFileNameToFetch = versionToFileName[versionToFetch]; - const versionDocObj = await docUtils.getJSONDocFileAsync( - versionFileNameToFetch, - this.props.docsInfo.docsJsonRoot, - ); - const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj); - - if (!this._isUnmounted) { - this.setState( - { - docAgnosticFormat, - }, - () => { - this._scrollToHash(); - }, - ); - } - } } diff --git a/packages/website/ts/pages/documentation/method_block.tsx b/packages/website/ts/pages/documentation/method_block.tsx index 1bc6aa4f4..d2c96bf8c 100644 --- a/packages/website/ts/pages/documentation/method_block.tsx +++ b/packages/website/ts/pages/documentation/method_block.tsx @@ -15,6 +15,7 @@ interface MethodBlockProps { libraryVersion: string; typeDefinitionByName: TypeDefinitionByName; docsInfo: DocsInfo; + sourceUrl: string; } interface MethodBlockState { @@ -80,8 +81,7 @@ export class MethodBlock extends React.Component<MethodBlockProps, MethodBlockSt <SourceLink version={this.props.libraryVersion} source={(method as TypescriptMethod).source} - baseUrl={this.props.docsInfo.packageUrl} - subPackageName={this.props.docsInfo.subPackageName} + sourceUrl={this.props.sourceUrl} /> )} {method.comment && <Comment comment={method.comment} className="py2" />} diff --git a/packages/website/ts/pages/documentation/source_link.tsx b/packages/website/ts/pages/documentation/source_link.tsx index 6588ee39e..31f80aba3 100644 --- a/packages/website/ts/pages/documentation/source_link.tsx +++ b/packages/website/ts/pages/documentation/source_link.tsx @@ -5,22 +5,13 @@ import { colors } from 'ts/utils/colors'; interface SourceLinkProps { source: Source; - baseUrl: string; + sourceUrl: string; version: string; - subPackageName: string; } -const packagesWithNamespace = ['connect']; - export function SourceLink(props: SourceLinkProps) { const src = props.source; - const url = props.baseUrl; - const pkg = props.subPackageName; - let tagPrefix = pkg; - if (_.includes(packagesWithNamespace, pkg)) { - tagPrefix = `@0xproject/${pkg}`; - } - const sourceCodeUrl = `${url}/blob/${tagPrefix}%40${props.version}/packages/${pkg}/${src.fileName}#L${src.line}`; + const sourceCodeUrl = `${props.sourceUrl}/${src.fileName}#L${src.line}`; return ( <div className="pt2" style={{ fontSize: 14 }}> <a href={sourceCodeUrl} target="_blank" className="underline" style={{ color: colors.grey }}> diff --git a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx index ba794ee9f..1a08ca9f9 100644 --- a/packages/website/ts/pages/shared/nested_sidebar_menu.tsx +++ b/packages/website/ts/pages/shared/nested_sidebar_menu.tsx @@ -16,7 +16,6 @@ interface NestedSidebarMenuProps { onMenuItemClick?: () => void; selectedVersion?: string; versions?: string[]; - docPath?: string; } interface NestedSidebarMenuState {} @@ -69,13 +68,8 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N <div> {this._renderEmblem()} {!_.isUndefined(this.props.versions) && - !_.isUndefined(this.props.selectedVersion) && - !_.isUndefined(this.props.docPath) && ( - <VersionDropDown - selectedVersion={this.props.selectedVersion} - versions={this.props.versions} - docPath={this.props.docPath} - /> + !_.isUndefined(this.props.selectedVersion) && ( + <VersionDropDown selectedVersion={this.props.selectedVersion} versions={this.props.versions} /> )} <div className="pl1">{navigation}</div> </div> diff --git a/packages/website/ts/pages/shared/version_drop_down.tsx b/packages/website/ts/pages/shared/version_drop_down.tsx index b922e1048..3b331af9b 100644 --- a/packages/website/ts/pages/shared/version_drop_down.tsx +++ b/packages/website/ts/pages/shared/version_drop_down.tsx @@ -6,7 +6,6 @@ import * as React from 'react'; interface VersionDropDownProps { selectedVersion: string; versions: string[]; - docPath: string; } interface VersionDropDownState {} @@ -31,7 +30,17 @@ export class VersionDropDown extends React.Component<VersionDropDownProps, Versi }); return items; } - private _updateSelectedVersion(e: any, index: number, value: string) { - window.location.href = `${this.props.docPath}/${value}${window.location.hash}`; + private _updateSelectedVersion(e: any, index: number, semver: string) { + const port = window.location.port; + const hasPort = !_.isUndefined(port); + let path = window.location.pathname; + const lastChar = path[path.length - 1]; + if (_.isFinite(_.parseInt(lastChar))) { + const pathSections = path.split('/'); + pathSections.pop(); + path = pathSections.join('/'); + } + const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}${path}`; + window.location.href = `${baseUrl}/${semver}${window.location.hash}`; } } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index f6413eec5..f0db537e6 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -627,20 +627,39 @@ export interface SectionsMap { [sectionName: string]: string; } +export enum DocPackages { + Connect = 'CONNECT', + ZeroExJs = 'ZERO_EX_JS', + SmartContracts = 'SMART_CONTRACTS', +} + +export enum SupportedDocJson { + Doxity = 'DOXITY', + TypeDoc = 'TYPEDOC', +} + +export interface ContractsByVersionByNetworkId { + [version: string]: { + [networkName: string]: { + [contractName: string]: string; + }; + }; +} + export interface DocsInfoConfig { + id: string; + type: SupportedDocJson; displayName: string; packageUrl: string; - websitePath: string; - docsJsonRoot: string; menu: DocsMenu; sections: SectionsMap; sectionNameToMarkdown: { [sectionName: string]: string }; visibleConstructors: string[]; - convertToDocAgnosticFormatFn: (docObj: DoxityDocObj | TypeDocNode, docsInfo?: any) => DocAgnosticFormat; subPackageName?: string; publicTypes?: string[]; sectionNameToModulePath?: { [sectionName: string]: string[] }; menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string }; + contractsByVersionByNetworkId?: ContractsByVersionByNetworkId; } export interface TimestampMsRange { diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index 8e359f8bd..7e9ba69de 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -1,12 +1,5 @@ import * as _ from 'lodash'; -import { - ContractAddresses, - Environments, - Networks, - OutdatedWrappedEtherByNetworkId, - PublicNodeUrlsByNetworkId, - SmartContractDocSections, -} from 'ts/types'; +import { ContractAddresses, Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types'; const BASE_URL = window.location.origin; const isDevelopment = _.includes( @@ -19,34 +12,6 @@ export const configs = { BACKEND_BASE_URL: 'https://website-api.0xproject.com', BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', - CONTRACT_ADDRESS: { - '1.0.0': { - [Networks.Mainnet]: { - [SmartContractDocSections.Exchange]: '0x12459c951127e0c374ff9105dda097662a027093', - [SmartContractDocSections.TokenTransferProxy]: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', - [SmartContractDocSections.ZRXToken]: '0xe41d2489571d322189246dafa5ebde1f4699f498', - [SmartContractDocSections.TokenRegistry]: '0x926a74c5c36adf004c87399e65f75628b0f98d2c', - }, - [Networks.Ropsten]: { - [SmartContractDocSections.Exchange]: '0x479cc461fecd078f766ecc58533d6f69580cf3ac', - [SmartContractDocSections.TokenTransferProxy]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', - [SmartContractDocSections.ZRXToken]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', - [SmartContractDocSections.TokenRegistry]: '0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed', - }, - [Networks.Kovan]: { - [SmartContractDocSections.Exchange]: '0x90fe2af704b34e0224bf2299c838e04d4dcf1364', - [SmartContractDocSections.TokenTransferProxy]: '0x087Eed4Bc1ee3DE49BeFbd66C662B434B15d49d4', - [SmartContractDocSections.ZRXToken]: '0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570', - [SmartContractDocSections.TokenRegistry]: '0xf18e504561f4347bea557f3d4558f559dddbae7f', - }, - [Networks.Rinkeby]: { - [SmartContractDocSections.Exchange]: '0x1d16ef40fac01cec8adac2ac49427b9384192c05', - [SmartContractDocSections.TokenTransferProxy]: '0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d', - [SmartContractDocSections.ZRXToken]: '0x00f58d6d585f84b2d7267940cede30ce2fe6eae8', - [SmartContractDocSections.TokenRegistry]: '0x4e9aad8184de8833365fea970cd9149372fdf1e6', - }, - }, - } as ContractAddresses, DEFAULT_DERIVATION_PATH: `44'/60'/0'`, // WARNING: ZRX & WETH MUST always be default trackedTokens DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'], diff --git a/packages/website/ts/utils/constants.ts b/packages/website/ts/utils/constants.ts index 6af821dbe..3476b7375 100644 --- a/packages/website/ts/utils/constants.ts +++ b/packages/website/ts/utils/constants.ts @@ -42,6 +42,7 @@ export const constants = { PROVIDER_NAME_GENERIC: 'Injected Web3', PROVIDER_NAME_PUBLIC: '0x Public', ROLLBAR_ACCESS_TOKEN: 'a6619002b51c4464928201e6ea94de65', + S3_BUCKET_ROOT: 'https://s3.amazonaws.com', SUCCESS_STATUS: 200, UNAVAILABLE_STATUS: 503, TAKER_FEE: new BigNumber(0), diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts index 11ec8da58..992475911 100644 --- a/packages/website/ts/utils/typedoc_utils.ts +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -4,6 +4,7 @@ import { CustomType, CustomTypeChild, DocAgnosticFormat, + DocPackages, DocSection, IndexSignature, KindString, @@ -108,7 +109,7 @@ export const typeDocUtils = { isConstructor, docsInfo.sections, sectionName, - docsInfo.subPackageName, + docsInfo.id, ); docSection.constructors.push(constructor); break; @@ -121,7 +122,7 @@ export const typeDocUtils = { isConstructor, docsInfo.sections, sectionName, - docsInfo.subPackageName, + docsInfo.id, ); docSection.methods.push(method); } @@ -133,7 +134,7 @@ export const typeDocUtils = { entity, docsInfo.sections, sectionName, - docsInfo.subPackageName, + docsInfo.id, ); docSection.properties.push(property); } @@ -149,7 +150,7 @@ export const typeDocUtils = { entity, docsInfo.sections, sectionName, - docsInfo.subPackageName, + docsInfo.id, ); docSection.types.push(customType); } @@ -161,21 +162,16 @@ export const typeDocUtils = { }); return docSection; }, - _convertCustomType( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): CustomType { + _convertCustomType(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): CustomType { const typeIfExists = !_.isUndefined(entity.type) - ? typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName) + ? typeDocUtils._convertType(entity.type, sections, sectionName, docId) : undefined; const isConstructor = false; const methodIfExists = !_.isUndefined(entity.declaration) - ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) + ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, docId) : undefined; const indexSignatureIfExists = !_.isUndefined(entity.indexSignature) - ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, subPackageName) + ? typeDocUtils._convertIndexSignature(entity.indexSignature[0], sections, sectionName, docId) : undefined; const commentIfExists = !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText) @@ -185,7 +181,7 @@ export const typeDocUtils = { const childrenIfExist = !_.isUndefined(entity.children) ? _.map(entity.children, (child: TypeDocNode) => { const childTypeIfExists = !_.isUndefined(child.type) - ? typeDocUtils._convertType(child.type, sections, sectionName, subPackageName) + ? typeDocUtils._convertType(child.type, sections, sectionName, docId) : undefined; const c: CustomTypeChild = { name: child.name, @@ -212,27 +208,22 @@ export const typeDocUtils = { entity: TypeDocNode, sections: SectionsMap, sectionName: string, - subPackageName: string, + docId: string, ): IndexSignature { const key = entity.parameters[0]; const indexSignature = { keyName: key.name, - keyType: typeDocUtils._convertType(key.type, sections, sectionName, subPackageName), + keyType: typeDocUtils._convertType(key.type, sections, sectionName, docId), valueName: entity.type.name, }; return indexSignature; }, - _convertProperty( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): Property { + _convertProperty(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): Property { const source = entity.sources[0]; const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined; const property = { name: entity.name, - type: typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName), + type: typeDocUtils._convertType(entity.type, sections, sectionName, docId), source: { fileName: source.fileName, line: source.line, @@ -246,7 +237,7 @@ export const typeDocUtils = { isConstructor: boolean, sections: SectionsMap, sectionName: string, - subPackageName: string, + docId: string, ): TypescriptMethod { const signature = entity.signatures[0]; const source = entity.sources[0]; @@ -258,7 +249,7 @@ export const typeDocUtils = { let callPath; if (isConstructor || entity.name === '__type') { callPath = ''; - } else if (subPackageName === '0x.js') { + } else if (docId === DocPackages.ZeroExJs) { const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.'; callPath = !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx @@ -269,12 +260,12 @@ export const typeDocUtils = { } const parameters = _.map(signature.parameters, param => { - return typeDocUtils._convertParameter(param, sections, sectionName, subPackageName); + return typeDocUtils._convertParameter(param, sections, sectionName, docId); }); - const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, subPackageName); + const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, docId); const typeParameter = _.isUndefined(signature.typeParameter) ? undefined - : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, subPackageName); + : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, docId); const method = { isConstructor, @@ -297,21 +288,16 @@ export const typeDocUtils = { entity: TypeDocNode, sections: SectionsMap, sectionName: string, - subPackageName: string, + docId: string, ): TypeParameter { - const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); + const type = typeDocUtils._convertType(entity.type, sections, sectionName, docId); const parameter = { name: entity.name, type, }; return parameter; }, - _convertParameter( - entity: TypeDocNode, - sections: SectionsMap, - sectionName: string, - subPackageName: string, - ): Parameter { + _convertParameter(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): Parameter { let comment = '<No comment>'; if (entity.comment && entity.comment.shortText) { comment = entity.comment.shortText; @@ -321,7 +307,7 @@ export const typeDocUtils = { const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false; - const type = typeDocUtils._convertType(entity.type, sections, sectionName, subPackageName); + const type = typeDocUtils._convertType(entity.type, sections, sectionName, docId); const parameter = { name: entity.name, @@ -331,17 +317,17 @@ export const typeDocUtils = { }; return parameter; }, - _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, subPackageName: string): Type { + _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, docId: string): Type { const typeArguments = _.map(entity.typeArguments, typeArgument => { - return typeDocUtils._convertType(typeArgument, sections, sectionName, subPackageName); + return typeDocUtils._convertType(typeArgument, sections, sectionName, docId); }); const types = _.map(entity.types, t => { - return typeDocUtils._convertType(t, sections, sectionName, subPackageName); + return typeDocUtils._convertType(t, sections, sectionName, docId); }); const isConstructor = false; const methodIfExists = !_.isUndefined(entity.declaration) - ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, subPackageName) + ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, docId) : undefined; const elementTypeIfExists = !_.isUndefined(entity.elementType) |