Skip to content

Commit 1f9aeae

Browse files
committed
refactor builder into plugin style
1 parent 5be19bb commit 1f9aeae

File tree

25 files changed

+986
-130
lines changed

25 files changed

+986
-130
lines changed

packages/ts-codegen/src/builder/builder.ts

Lines changed: 69 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
import { RenderOptions, defaultOptions } from "wasm-ast-types";
1+
import { RenderOptions, defaultOptions, RenderContext, ContractInfo, MessageComposerOptions} from "wasm-ast-types";
22

33
import { header } from '../utils/header';
44
import { join } from "path";
55
import { writeFileSync } from 'fs';
66
import { sync as mkdirp } from "mkdirp";
77

8-
import generateMessageComposer from '../generators/message-composer';
9-
import generateMsgBuilder from '../generators/msg-builder';
10-
import generateTypes from '../generators/types';
11-
import generateReactQuery from '../generators/react-query';
12-
import generateRecoil from '../generators/recoil';
13-
import generateClient from '../generators/client';
14-
158
import { basename } from 'path';
169
import { readSchemas } from '../utils';
10+
import { IBuilderPlugin } from '../plugins';
1711

1812
import deepmerge from 'deepmerge';
1913
import { pascal } from "case";
2014
import { createFileBundle, recursiveModuleBundle } from "../bundler";
2115

2216
import generate from '@babel/generator';
2317
import * as t from '@babel/types';
18+
import { ReactQueryPlugin } from "../plugins/react-query";
19+
import { RecoilPlugin } from "../plugins/recoil";
20+
import { MsgBuilderPlugin } from "../plugins/msg-builder";
21+
import { MessageComposerPlugin } from "../plugins/message-composer";
22+
import { ClientPlugin } from "../plugins/client";
23+
import { TypesPlugin } from "../plugins/types";
2424

2525
const defaultOpts: TSBuilderOptions = {
2626
bundle: {
@@ -34,6 +34,7 @@ export interface TSBuilderInput {
3434
contracts: Array<ContractFile | string>;
3535
outPath: string;
3636
options?: TSBuilderOptions;
37+
plugins?: IBuilderPlugin[];
3738
};
3839

3940
export interface BundleOptions {
@@ -47,8 +48,11 @@ export type TSBuilderOptions = {
4748
bundle?: BundleOptions;
4849
} & RenderOptions;
4950

51+
export type BuilderFileType = 'type' | 'client' | 'recoil' | 'react-query' | 'message-composer' | 'msg-builder' | 'plugin';
52+
5053
export interface BuilderFile {
51-
type: 'type' | 'client' | 'recoil' | 'react-query' | 'message-composer' | 'msg-builder';
54+
type: BuilderFileType;
55+
pluginType?: string;
5256
contract: string;
5357
localname: string;
5458
filename: string;
@@ -58,14 +62,42 @@ export interface ContractFile {
5862
name: string;
5963
dir: string;
6064
}
65+
66+
function getContract(contractOpt): ContractFile {
67+
if (typeof contractOpt === 'string') {
68+
const name = basename(contractOpt);
69+
const contractName = pascal(name);
70+
return {
71+
name: contractName,
72+
dir: contractOpt
73+
}
74+
}
75+
return {
76+
name: pascal(contractOpt.name),
77+
dir: contractOpt.dir
78+
};
79+
}
80+
6181
export class TSBuilder {
6282
contracts: Array<ContractFile | string>;
6383
outPath: string;
6484
options?: TSBuilderOptions;
85+
plugins: IBuilderPlugin[] = [];
6586

6687
protected files: BuilderFile[] = [];
6788

68-
constructor({ contracts, outPath, options }: TSBuilderInput) {
89+
loadDefaultPlugins() {
90+
[].push.apply(this.plugins, [
91+
new TypesPlugin(this.options),
92+
new ClientPlugin(this.options),
93+
new MessageComposerPlugin(this.options),
94+
new ReactQueryPlugin(this.options),
95+
new RecoilPlugin(this.options),
96+
new MsgBuilderPlugin(this.options),
97+
]);
98+
}
99+
100+
constructor({ contracts, outPath, options, plugins }: TSBuilderInput) {
69101
this.contracts = contracts;
70102
this.outPath = outPath;
71103
this.options = deepmerge(
@@ -75,96 +107,43 @@ export class TSBuilder {
75107
),
76108
options ?? {}
77109
);
78-
}
79-
80-
getContracts(): ContractFile[] {
81-
return this.contracts.map(contractOpt => {
82-
if (typeof contractOpt === 'string') {
83-
const name = basename(contractOpt);
84-
const contractName = pascal(name);
85-
return {
86-
name: contractName,
87-
dir: contractOpt
88-
}
89-
}
90-
return {
91-
name: pascal(contractOpt.name),
92-
dir: contractOpt.dir
93-
};
94-
});
95-
}
96110

97-
async renderTypes(contract: ContractFile) {
98-
const { enabled, ...options } = this.options.types;
99-
if (!enabled) return;
100-
const contractInfo = await readSchemas({
101-
schemaDir: contract.dir
102-
});
103-
const files = await generateTypes(contract.name, contractInfo, this.outPath, options);
104-
[].push.apply(this.files, files);
105-
}
111+
this.loadDefaultPlugins();
106112

107-
async renderClient(contract: ContractFile) {
108-
const { enabled, ...options } = this.options.client;
109-
if (!enabled) return;
110-
const contractInfo = await readSchemas({
111-
schemaDir: contract.dir
112-
});
113-
const files = await generateClient(contract.name, contractInfo, this.outPath, options);
114-
[].push.apply(this.files, files);
115-
}
116-
117-
async renderRecoil(contract: ContractFile) {
118-
const { enabled, ...options } = this.options.recoil;
119-
if (!enabled) return;
120-
const contractInfo = await readSchemas({
121-
schemaDir: contract.dir
122-
});
123-
const files = await generateRecoil(contract.name, contractInfo, this.outPath, options);
124-
[].push.apply(this.files, files);
113+
if (plugins && plugins.length) {
114+
[].push.apply(this.plugins, plugins);
115+
}
125116
}
126117

127-
async renderReactQuery(contract: ContractFile) {
128-
const { enabled, ...options } = this.options.reactQuery;
129-
if (!enabled) return;
130-
const contractInfo = await readSchemas({
131-
schemaDir: contract.dir
132-
});
133-
const files = await generateReactQuery(contract.name, contractInfo, this.outPath, options);
134-
[].push.apply(this.files, files);
118+
async build() {
119+
await this.process();
120+
await this.after();
135121
}
136122

137-
async renderMessageComposer(contract: ContractFile) {
138-
const { enabled, ...options } = this.options.messageComposer;
139-
if (!enabled) return;
140-
const contractInfo = await readSchemas({
141-
schemaDir: contract.dir
142-
});
143-
const files = await generateMessageComposer(contract.name, contractInfo, this.outPath, options);
144-
[].push.apply(this.files, files);
123+
// lifecycle functions
124+
private async process(){
125+
for (const contractOpt of this.contracts) {
126+
const contract = getContract(contractOpt);
127+
//resolve contract schema.
128+
const contractInfo = await readSchemas({
129+
schemaDir: contract.dir
130+
});
131+
132+
//lifecycle and plugins.
133+
await this.render(contract.name, contractInfo);
134+
}
145135
}
146136

147-
async renderMsgBuilder(contract: ContractFile) {
148-
const { enabled, ...options } = this.options.messageComposer;
149-
if (!enabled) return;
150-
const contractInfo = await readSchemas({
151-
schemaDir: contract.dir
152-
});
153-
const files = await generateMsgBuilder(contract.name, contractInfo, this.outPath, options);
154-
[].push.apply(this.files, files);
137+
private async render(name: string, contractInfo: ContractInfo){
138+
for (const plugin of this.plugins) {
139+
let files = await plugin.render(name, contractInfo, this.outPath);
140+
if(files && files.length){
141+
[].push.apply(this.files, files);
142+
}
143+
}
155144
}
156145

157-
async build() {
158-
const contracts = this.getContracts();
159-
for (let c = 0; c < contracts.length; c++) {
160-
const contract = contracts[c];
161-
await this.renderTypes(contract);
162-
await this.renderClient(contract);
163-
await this.renderMessageComposer(contract);
164-
await this.renderMsgBuilder(contract);
165-
await this.renderReactQuery(contract);
166-
await this.renderRecoil(contract);
167-
}
146+
private async after(){
168147
if (this.options.bundle.enabled) {
169148
this.bundle();
170149
}

packages/ts-codegen/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export { default as generateRecoil } from './generators/recoil';
99
export * from './utils';
1010
export * from './builder';
1111
export * from './bundler';
12+
export * from './plugins';
1213

1314
export default async (input: TSBuilderInput) => {
1415
const builder = new TSBuilder(input);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { pascal } from 'case';
2+
import * as w from 'wasm-ast-types';
3+
import { findExecuteMsg, findAndParseTypes, findQueryMsg } from '../utils';
4+
import {
5+
RenderContext,
6+
ContractInfo,
7+
RenderContextBase,
8+
getMessageProperties,
9+
RenderOptions
10+
} from 'wasm-ast-types';
11+
import { BuilderFileType } from '../builder';
12+
import { BuilderPluginBase } from './plugin-base';
13+
14+
export class ClientPlugin extends BuilderPluginBase<RenderOptions> {
15+
initContext(
16+
contract: ContractInfo,
17+
options?: RenderOptions
18+
): RenderContextBase<RenderOptions> {
19+
return new RenderContext(contract, options);
20+
}
21+
22+
async doRender(
23+
name: string,
24+
context: RenderContext
25+
): Promise<
26+
{
27+
type: BuilderFileType;
28+
pluginType?: string;
29+
localname: string;
30+
body: any[];
31+
}[]
32+
> {
33+
const { enabled } = this.option.client;
34+
35+
if (!enabled) {
36+
return;
37+
}
38+
39+
const { schemas } = context.contract;
40+
41+
const localname = pascal(name) + '.client.ts';
42+
const TypesFile = pascal(name) + '.types';
43+
const QueryMsg = findQueryMsg(schemas);
44+
const ExecuteMsg = findExecuteMsg(schemas);
45+
const typeHash = await findAndParseTypes(schemas);
46+
47+
let Client = null;
48+
let Instance = null;
49+
let QueryClient = null;
50+
let ReadOnlyInstance = null;
51+
52+
const body = [];
53+
54+
body.push(w.importStmt(Object.keys(typeHash), `./${TypesFile}`));
55+
56+
// query messages
57+
if (QueryMsg) {
58+
QueryClient = pascal(`${name}QueryClient`);
59+
ReadOnlyInstance = pascal(`${name}ReadOnlyInterface`);
60+
61+
body.push(w.createQueryInterface(context, ReadOnlyInstance, QueryMsg));
62+
body.push(
63+
w.createQueryClass(context, QueryClient, ReadOnlyInstance, QueryMsg)
64+
);
65+
}
66+
67+
// execute messages
68+
if (ExecuteMsg) {
69+
const children = getMessageProperties(ExecuteMsg);
70+
if (children.length > 0) {
71+
Client = pascal(`${name}Client`);
72+
Instance = pascal(`${name}Interface`);
73+
74+
body.push(
75+
w.createExecuteInterface(
76+
context,
77+
Instance,
78+
this.option.client.execExtendsQuery ? ReadOnlyInstance : null,
79+
ExecuteMsg
80+
)
81+
);
82+
83+
body.push(
84+
w.createExecuteClass(
85+
context,
86+
Client,
87+
Instance,
88+
this.option.client.execExtendsQuery ? QueryClient : null,
89+
ExecuteMsg
90+
)
91+
);
92+
}
93+
}
94+
95+
if (typeHash.hasOwnProperty('Coin')) {
96+
// @ts-ignore
97+
delete context.utils.Coin;
98+
}
99+
100+
return [
101+
{
102+
type: 'client',
103+
localname,
104+
body
105+
}
106+
];
107+
}
108+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./plugin-base"

0 commit comments

Comments
 (0)