parent
d4e187f563
commit
0368d26a7d
@ -1,17 +1,23 @@ |
||||
import React from "react" |
||||
import { PluginApi, IRemixApi, Api, PluginClient, CompilationResult } from "@remixproject/plugin" |
||||
import React from "react"; |
||||
import { |
||||
PluginApi, |
||||
IRemixApi, |
||||
Api, |
||||
PluginClient, |
||||
CompilationResult, |
||||
} from "@remixproject/plugin"; |
||||
|
||||
import { ContractName, Documentation, PublishedSite } from "./types" |
||||
import { ContractName, Documentation, PublishedSite } from "./types"; |
||||
|
||||
export const AppContext = React.createContext({ |
||||
clientInstance: {} as PluginApi<Readonly<IRemixApi>> & |
||||
PluginClient<Api, Readonly<IRemixApi>>, |
||||
contracts: new Map<ContractName, Documentation>(), |
||||
setContracts: (contracts: Map<ContractName, Documentation>) => { |
||||
console.log("Calling Set Contract Names") |
||||
console.log("Calling Set Contract Names"); |
||||
}, |
||||
sites: [], |
||||
setSites: (sites: PublishedSite[]) => { |
||||
console.log("Calling Set Sites") |
||||
} |
||||
}) |
||||
console.log("Calling Set Sites"); |
||||
}, |
||||
}); |
||||
|
@ -1,11 +1,11 @@ |
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom'; |
||||
import App from './App'; |
||||
import { Routes } from './routes' |
||||
import React from "react"; |
||||
import ReactDOM from "react-dom"; |
||||
import App from "./App"; |
||||
import { Routes } from "./routes"; |
||||
|
||||
ReactDOM.render( |
||||
<React.StrictMode> |
||||
<App /> |
||||
</React.StrictMode>, |
||||
document.getElementById('root') |
||||
); |
||||
document.getElementById("root") |
||||
); |
||||
|
@ -1,36 +1,31 @@ |
||||
import React from "react" |
||||
import React from "react"; |
||||
import { |
||||
BrowserRouter as Router, |
||||
Switch, |
||||
Route, |
||||
RouteProps, |
||||
} from "react-router-dom" |
||||
BrowserRouter as Router, |
||||
Switch, |
||||
Route, |
||||
RouteProps, |
||||
} from "react-router-dom"; |
||||
|
||||
import { ErrorView, HomeView } from "./views" |
||||
import { ErrorView, HomeView } from "./views"; |
||||
|
||||
interface Props extends RouteProps { |
||||
component: any // TODO: new (props: any) => React.Component
|
||||
from: string |
||||
component: any; // TODO: new (props: any) => React.Component
|
||||
from: string; |
||||
} |
||||
|
||||
const CustomRoute = ({ component: Component, ...rest }: Props) => { |
||||
return ( |
||||
<Route |
||||
{...rest} |
||||
render={(matchProps) => ( |
||||
<Component {...matchProps} /> |
||||
)} |
||||
/> |
||||
) |
||||
} |
||||
return ( |
||||
<Route {...rest} render={(matchProps) => <Component {...matchProps} />} /> |
||||
); |
||||
}; |
||||
|
||||
export const Routes = () => ( |
||||
<Router> |
||||
<Switch> |
||||
<CustomRoute exact path="/" component={HomeView} from="/" /> |
||||
<Route path="/error"> |
||||
<ErrorView /> |
||||
</Route> |
||||
</Switch> |
||||
</Router> |
||||
) |
||||
<Router> |
||||
<Switch> |
||||
<CustomRoute exact path="/" component={HomeView} from="/" /> |
||||
<Route path="/error"> |
||||
<ErrorView /> |
||||
</Route> |
||||
</Switch> |
||||
</Router> |
||||
); |
||||
|
@ -1,30 +1,30 @@ |
||||
import { CompiledContract, ABIParameter } from '@remixproject/plugin' |
||||
import { CompiledContract, ABIParameter } from "@remixproject/plugin"; |
||||
|
||||
import sampleData from './sample-data/sample-artifact.json' |
||||
import sampleDataWithComments from './sample-data/sample-artifact-with-comments.json' |
||||
import sampleData from "./sample-data/sample-artifact.json"; |
||||
import sampleDataWithComments from "./sample-data/sample-artifact-with-comments.json"; |
||||
|
||||
export const buildFakeArtifact: () => CompiledContract = () => { |
||||
const result = sampleData as never as CompiledContract |
||||
return result |
||||
} |
||||
const result = (sampleData as never) as CompiledContract; |
||||
return result; |
||||
}; |
||||
|
||||
export const buildFakeArtifactWithComments: () => CompiledContract = () => { |
||||
const result = sampleDataWithComments as never as CompiledContract |
||||
return result |
||||
} |
||||
const result = (sampleDataWithComments as never) as CompiledContract; |
||||
return result; |
||||
}; |
||||
|
||||
export const buildFakeABIParameter: () => ABIParameter = () => { |
||||
return { |
||||
internalType: "address", |
||||
name: "allocator", |
||||
type: "address" |
||||
} |
||||
} |
||||
return { |
||||
internalType: "address", |
||||
name: "allocator", |
||||
type: "address", |
||||
}; |
||||
}; |
||||
|
||||
export const buildFakeABIParameterWithDocumentation: () => ABIParameter = () => { |
||||
return { |
||||
internalType: "address", |
||||
name: "allocator", |
||||
type: "address" |
||||
} |
||||
} |
||||
return { |
||||
internalType: "address", |
||||
name: "allocator", |
||||
type: "address", |
||||
}; |
||||
}; |
||||
|
@ -1,2 +1,2 @@ |
||||
export * from './utils' |
||||
export * from './publisher' |
||||
export * from "./utils"; |
||||
export * from "./publisher"; |
||||
|
@ -1,13 +1,18 @@ |
||||
import { HTMLContent } from "./types"; |
||||
|
||||
const IpfsClient = require('ipfs-mini') |
||||
// tslint:disable-next-line
|
||||
const IpfsClient = require("ipfs-mini"); |
||||
|
||||
export const publish = async (content: HTMLContent) => { |
||||
const ipfs = new IpfsClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' }); |
||||
const ipfs = new IpfsClient({ |
||||
host: "ipfs.infura.io", |
||||
port: 5001, |
||||
protocol: "https", |
||||
}); |
||||
|
||||
const documentHash = await ipfs.add(content) |
||||
const documentHash = await ipfs.add(content); |
||||
|
||||
console.log("Document hash", documentHash) |
||||
console.log("Document hash", documentHash); |
||||
|
||||
return documentHash |
||||
} |
||||
return documentHash; |
||||
}; |
||||
|
@ -1,36 +1,41 @@ |
||||
import { UserMethodDoc, DevMethodDoc, DeveloperDocumentation, UserDocumentation } from "@remixproject/plugin"; |
||||
import { |
||||
UserMethodDoc, |
||||
DevMethodDoc, |
||||
DeveloperDocumentation, |
||||
UserDocumentation, |
||||
} from "@remixproject/plugin"; |
||||
|
||||
export interface MethodsDocumentation { |
||||
[x: string]: UserMethodDoc | DevMethodDoc |
||||
[x: string]: UserMethodDoc | DevMethodDoc; |
||||
} |
||||
|
||||
export interface ContractDocumentation { |
||||
methods: MethodsDocumentation; |
||||
author: string; |
||||
title: string; |
||||
details: string; |
||||
notice: string; |
||||
methods: MethodsDocumentation; |
||||
author: string; |
||||
title: string; |
||||
details: string; |
||||
notice: string; |
||||
} |
||||
|
||||
export type MethodDoc = DevMethodDoc & UserMethodDoc |
||||
export type MethodDoc = DevMethodDoc & UserMethodDoc; |
||||
|
||||
export type TemplateDoc<T> = { [key in keyof T]: (...params: any[]) => string } |
||||
export type TemplateDoc<T> = { [key in keyof T]: (...params: any[]) => string }; |
||||
|
||||
// Contract
|
||||
export type ContractDoc = DeveloperDocumentation & UserDocumentation |
||||
export type ContractDoc = DeveloperDocumentation & UserDocumentation; |
||||
|
||||
export interface FunctionDocumentation { |
||||
name: string |
||||
type: string |
||||
devdoc?: Partial<MethodDoc> |
||||
inputs: ParameterDocumentation[] |
||||
outputs: ParameterDocumentation[] |
||||
name: string; |
||||
type: string; |
||||
devdoc?: Partial<MethodDoc>; |
||||
inputs: ParameterDocumentation[]; |
||||
outputs: ParameterDocumentation[]; |
||||
} |
||||
|
||||
export interface ParameterDocumentation { |
||||
name: string |
||||
type: string |
||||
description: string |
||||
name: string; |
||||
type: string; |
||||
description: string; |
||||
} |
||||
|
||||
export type HTMLContent = string |
||||
export type HTMLContent = string; |
||||
|
@ -1,93 +1,99 @@ |
||||
const open = require('open') |
||||
|
||||
import { getContractDoc, mergeParametersWithDevdoc, getFunctionDocumentation, getContractDocumentation } from './utils'; |
||||
import { FunctionDescription } from '@remixproject/plugin'; |
||||
import { buildFakeArtifactWithComments, buildFakeABIParameter } from './faker' |
||||
|
||||
jest.setTimeout(10000) |
||||
|
||||
describe('Publisher tests', () => { |
||||
|
||||
describe('getContractDocumentation', () => { |
||||
test('getContractDocumentation', () => { |
||||
const result = getContractDocumentation(buildFakeArtifactWithComments()) |
||||
|
||||
const result2 = { |
||||
methods: |
||||
{ |
||||
'age(uint256)': |
||||
{ |
||||
author: 'Mary A. Botanist', |
||||
details: |
||||
'The Alexandr N. Tetearing algorithm could increase precision', |
||||
params: [Object], |
||||
return: 'age in years, rounded up for partial years' |
||||
} |
||||
}, |
||||
notice: |
||||
'You can use this contract for only the most basic simulation', |
||||
author: 'Larry A. Gardner', |
||||
details: |
||||
'All function calls are currently implemented without side effects', |
||||
title: 'A simulator for trees' |
||||
} |
||||
|
||||
expect(result).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('getContractDoc', () => { |
||||
test('getContractDoc', () => { |
||||
|
||||
const template = getContractDoc("Fallout", buildFakeArtifactWithComments()); |
||||
|
||||
console.log("Template", template) |
||||
|
||||
expect(template).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('getFunctionDocumentation', () => { |
||||
test('getFunctionDocumentation', () => { |
||||
const abiItem: FunctionDescription = { |
||||
constant: false, |
||||
inputs: [], |
||||
name: "Fal1out", |
||||
outputs: [], |
||||
payable: true, |
||||
stateMutability: "payable", |
||||
type: "function" |
||||
} |
||||
|
||||
const result = getFunctionDocumentation(abiItem, {}) |
||||
|
||||
expect(result).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('mergeParametersWithDevdoc', () => { |
||||
test('mergeParametersWithDevdoc', () => { |
||||
const abiParameters = [buildFakeABIParameter()] |
||||
const devParams = {} |
||||
const result = mergeParametersWithDevdoc(abiParameters, devParams) |
||||
|
||||
expect(result.length).toEqual(1) |
||||
}) |
||||
|
||||
test('mergeParametersWithDevdoc with documentation', () => { |
||||
const abiParameters = [buildFakeABIParameter()] |
||||
const devParams = {} |
||||
const result = mergeParametersWithDevdoc(abiParameters, devParams) |
||||
|
||||
expect(result.length).toEqual(1) |
||||
}) |
||||
}) |
||||
|
||||
|
||||
test.skip('html generation', async () => { |
||||
await open('https://ipfs.io/ipfs/QmPYQyWyTrUZt3tjiPsEnkRQxedChYUjgEk9zLQ36SfpyW', { app: ['google chrome', '--incognito'] }); |
||||
// start server
|
||||
// generate html
|
||||
// server it
|
||||
}) |
||||
}) |
||||
// tslint:disable-next-line
|
||||
const open = require("open"); |
||||
|
||||
import { |
||||
getContractDoc, |
||||
mergeParametersWithDevdoc, |
||||
getFunctionDocumentation, |
||||
getContractDocumentation, |
||||
} from "./utils"; |
||||
import { FunctionDescription } from "@remixproject/plugin"; |
||||
import { buildFakeArtifactWithComments, buildFakeABIParameter } from "./faker"; |
||||
|
||||
jest.setTimeout(10000); |
||||
|
||||
describe("Publisher tests", () => { |
||||
describe("getContractDocumentation", () => { |
||||
test("getContractDocumentation", () => { |
||||
const result = getContractDocumentation(buildFakeArtifactWithComments()); |
||||
|
||||
const result2 = { |
||||
methods: { |
||||
"age(uint256)": { |
||||
author: "Mary A. Botanist", |
||||
details: |
||||
"The Alexandr N. Tetearing algorithm could increase precision", |
||||
params: [Object], |
||||
return: "age in years, rounded up for partial years", |
||||
}, |
||||
}, |
||||
notice: "You can use this contract for only the most basic simulation", |
||||
author: "Larry A. Gardner", |
||||
details: |
||||
"All function calls are currently implemented without side effects", |
||||
title: "A simulator for trees", |
||||
}; |
||||
|
||||
expect(result).toBeDefined(); |
||||
}); |
||||
}); |
||||
|
||||
describe("getContractDoc", () => { |
||||
test("getContractDoc", () => { |
||||
const template = getContractDoc( |
||||
"Fallout", |
||||
buildFakeArtifactWithComments() |
||||
); |
||||
|
||||
console.log("Template", template); |
||||
|
||||
expect(template).toBeDefined(); |
||||
}); |
||||
}); |
||||
|
||||
describe("getFunctionDocumentation", () => { |
||||
test("getFunctionDocumentation", () => { |
||||
const abiItem: FunctionDescription = { |
||||
constant: false, |
||||
inputs: [], |
||||
name: "Fal1out", |
||||
outputs: [], |
||||
payable: true, |
||||
stateMutability: "payable", |
||||
type: "function", |
||||
}; |
||||
|
||||
const result = getFunctionDocumentation(abiItem, {}); |
||||
|
||||
expect(result).toBeDefined(); |
||||
}); |
||||
}); |
||||
|
||||
describe("mergeParametersWithDevdoc", () => { |
||||
test("mergeParametersWithDevdoc", () => { |
||||
const abiParameters = [buildFakeABIParameter()]; |
||||
const devParams = {}; |
||||
const result = mergeParametersWithDevdoc(abiParameters, devParams); |
||||
|
||||
expect(result.length).toEqual(1); |
||||
}); |
||||
|
||||
test("mergeParametersWithDevdoc with documentation", () => { |
||||
const abiParameters = [buildFakeABIParameter()]; |
||||
const devParams = {}; |
||||
const result = mergeParametersWithDevdoc(abiParameters, devParams); |
||||
|
||||
expect(result.length).toEqual(1); |
||||
}); |
||||
}); |
||||
|
||||
test.skip("html generation", async () => { |
||||
await open( |
||||
"https://ipfs.io/ipfs/QmPYQyWyTrUZt3tjiPsEnkRQxedChYUjgEk9zLQ36SfpyW", |
||||
{ app: ["google chrome", "--incognito"] } |
||||
); |
||||
// start server
|
||||
// generate html
|
||||
// server it
|
||||
}); |
||||
}); |
||||
|
@ -1,94 +1,130 @@ |
||||
import { CompilationResult, CompiledContract, FunctionDescription, ABIDescription, DevMethodDoc, UserMethodDoc, ABIParameter, DeveloperDocumentation, UserDocumentation } from "@remixproject/plugin" |
||||
|
||||
import { EthDocumentation, FileName, Documentation, ContractName } from '../types' |
||||
import { template } from './template' |
||||
import { ContractDocumentation, MethodDoc, FunctionDocumentation, ParameterDocumentation, MethodsDocumentation } from './types' |
||||
|
||||
export const createDocumentation = (fileName: FileName, compilationResult: CompilationResult) => { |
||||
console.log("Filename", fileName) |
||||
const result = new Map<ContractName, Documentation>(); |
||||
|
||||
const contracts = compilationResult.contracts[fileName] |
||||
console.log("Contracts", contracts) |
||||
|
||||
Object.keys(contracts).forEach((name) => { |
||||
console.log("CompiledContract", JSON.stringify(contracts[name])) |
||||
result.set(name, getContractDoc(name, contracts[name])) |
||||
}) |
||||
|
||||
return result |
||||
} |
||||
import { |
||||
CompilationResult, |
||||
CompiledContract, |
||||
FunctionDescription, |
||||
ABIDescription, |
||||
DevMethodDoc, |
||||
UserMethodDoc, |
||||
ABIParameter, |
||||
DeveloperDocumentation, |
||||
UserDocumentation, |
||||
} from "@remixproject/plugin"; |
||||
|
||||
import { |
||||
EthDocumentation, |
||||
FileName, |
||||
Documentation, |
||||
ContractName, |
||||
} from "../types"; |
||||
import { template } from "./template"; |
||||
import { |
||||
ContractDocumentation, |
||||
MethodDoc, |
||||
FunctionDocumentation, |
||||
ParameterDocumentation, |
||||
MethodsDocumentation, |
||||
} from "./types"; |
||||
|
||||
export const createDocumentation = ( |
||||
fileName: FileName, |
||||
compilationResult: CompilationResult |
||||
) => { |
||||
console.log("Filename", fileName); |
||||
const result = new Map<ContractName, Documentation>(); |
||||
|
||||
const contracts = compilationResult.contracts[fileName]; |
||||
console.log("Contracts", contracts); |
||||
|
||||
Object.keys(contracts).forEach((name) => { |
||||
console.log("CompiledContract", JSON.stringify(contracts[name])); |
||||
result.set(name, getContractDoc(name, contracts[name])); |
||||
}); |
||||
|
||||
return result; |
||||
}; |
||||
|
||||
export const getContractDoc = (name: string, contract: CompiledContract) => { |
||||
const contractDoc: ContractDocumentation = getContractDocumentation(contract) |
||||
|
||||
const functionsDocumentation = contract.abi |
||||
.map((def: ABIDescription) => { |
||||
if (def.type === 'constructor') { |
||||
def.name = 'constructor' |
||||
// because "constructor" is a string and not a { notice } object for userdoc we need to do that
|
||||
const methodDoc = { |
||||
...(contract.devdoc.methods.constructor || {}), |
||||
notice: contract.userdoc.methods.constructor as string |
||||
} |
||||
return getFunctionDocumentation(def, methodDoc) |
||||
} else { |
||||
if (def.type === 'fallback') { |
||||
def.name = 'fallback' |
||||
} |
||||
const method = Object.keys(contractDoc.methods).find((key) => key.includes(def.name as string)) as string |
||||
const methodDoc = contractDoc.methods[method] |
||||
return getFunctionDocumentation(def as FunctionDescription, methodDoc) |
||||
} |
||||
}) |
||||
|
||||
return template(name, contractDoc, functionsDocumentation) |
||||
} |
||||
const contractDoc: ContractDocumentation = getContractDocumentation(contract); |
||||
|
||||
const functionsDocumentation = contract.abi.map((def: ABIDescription) => { |
||||
if (def.type === "constructor") { |
||||
def.name = "constructor"; |
||||
// because "constructor" is a string and not a { notice } object for userdoc we need to do that
|
||||
const methodDoc = { |
||||
...(contract.devdoc.methods.constructor || {}), |
||||
notice: contract.userdoc.methods.constructor as string, |
||||
}; |
||||
return getFunctionDocumentation(def, methodDoc); |
||||
} else { |
||||
if (def.type === "fallback") { |
||||
def.name = "fallback"; |
||||
} |
||||
const method = Object.keys(contractDoc.methods).find((key) => |
||||
key.includes(def.name as string) |
||||
) as string; |
||||
const methodDoc = contractDoc.methods[method]; |
||||
return getFunctionDocumentation(def as FunctionDescription, methodDoc); |
||||
} |
||||
}); |
||||
|
||||
return template(name, contractDoc, functionsDocumentation); |
||||
}; |
||||
|
||||
export const getContractDocumentation = (contract: CompiledContract) => { |
||||
let methods: MethodsDocumentation = {}; |
||||
|
||||
Object.keys(contract.userdoc.methods).map((item) => { |
||||
if (contract.devdoc.methods[item]) { |
||||
const finalResult = { |
||||
...contract.userdoc.methods[item], |
||||
...contract.devdoc.methods[item] |
||||
} |
||||
methods[item] = finalResult |
||||
} else { |
||||
methods[item] = contract.userdoc.methods[item] |
||||
} |
||||
}) |
||||
|
||||
const contractDoc = { ...contract.userdoc, ...contract.devdoc, methods } |
||||
|
||||
return contractDoc |
||||
} |
||||
|
||||
export const getFunctionDocumentation = (def: FunctionDescription, devdoc?: Partial<MethodDoc>) => { |
||||
const doc = devdoc || {} |
||||
const devparams = doc.params || {} |
||||
const inputsWithDescription = mergeParametersWithDevdoc(def.inputs || [], devparams) |
||||
const outputsWithDescription = mergeParametersWithDevdoc(def.outputs || [], devparams) |
||||
const type = def.constant ? 'view' : 'read' |
||||
|
||||
const methods: MethodsDocumentation = {}; |
||||
|
||||
Object.keys(contract.userdoc.methods).map((item) => { |
||||
if (contract.devdoc.methods[item]) { |
||||
const finalResult = { |
||||
...contract.userdoc.methods[item], |
||||
...contract.devdoc.methods[item], |
||||
}; |
||||
methods[item] = finalResult; |
||||
} else { |
||||
methods[item] = contract.userdoc.methods[item]; |
||||
} |
||||
}); |
||||
|
||||
const contractDoc = { ...contract.userdoc, ...contract.devdoc, methods }; |
||||
|
||||
return contractDoc; |
||||
}; |
||||
|
||||
export const getFunctionDocumentation = ( |
||||
def: FunctionDescription, |
||||
devdoc?: Partial<MethodDoc> |
||||
) => { |
||||
const doc = devdoc || {}; |
||||
const devparams = doc.params || {}; |
||||
const inputsWithDescription = mergeParametersWithDevdoc( |
||||
def.inputs || [], |
||||
devparams |
||||
); |
||||
const outputsWithDescription = mergeParametersWithDevdoc( |
||||
def.outputs || [], |
||||
devparams |
||||
); |
||||
const type = def.constant ? "view" : "read"; |
||||
|
||||
return { |
||||
name: def.name, |
||||
type, |
||||
devdoc, |
||||
inputs: inputsWithDescription, |
||||
outputs: outputsWithDescription, |
||||
} as FunctionDocumentation; |
||||
}; |
||||
|
||||
export const mergeParametersWithDevdoc = ( |
||||
params: ABIParameter[], |
||||
devparams: any |
||||
) => { |
||||
return params.map((input) => { |
||||
const description = devparams[input.name] || ""; |
||||
return { |
||||
name: def.name, |
||||
type, |
||||
devdoc: devdoc, |
||||
inputs: inputsWithDescription, |
||||
outputs: outputsWithDescription |
||||
} as FunctionDocumentation |
||||
} |
||||
|
||||
export const mergeParametersWithDevdoc = (params: ABIParameter[], devparams: any) => { |
||||
return params.map((input) => { |
||||
const description = devparams[input.name] || '' |
||||
return { |
||||
name: input.name, |
||||
type: input.type, |
||||
description |
||||
} as ParameterDocumentation |
||||
}) |
||||
} |
||||
|
||||
name: input.name, |
||||
type: input.type, |
||||
description, |
||||
} as ParameterDocumentation; |
||||
}); |
||||
}; |
||||
|
@ -1,2 +1,2 @@ |
||||
export { HomeView } from "./HomeView" |
||||
export { ErrorView } from "./ErrorView" |
||||
export { HomeView } from "./HomeView"; |
||||
export { ErrorView } from "./ErrorView"; |
||||
|
Loading…
Reference in new issue