@cosmwasm/ts-codegen 0.30.0 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. package/README.md +5 -2
  2. package/main/builder/builder.js +41 -4
  3. package/main/generators/create-helpers.js +38 -0
  4. package/main/helpers/contractContextBase.js +8 -0
  5. package/main/helpers/contractsContextTSX.js +8 -0
  6. package/main/helpers/index.js +31 -0
  7. package/main/plugins/client.js +11 -6
  8. package/main/plugins/message-composer.js +10 -6
  9. package/main/plugins/msg-builder.js +1 -1
  10. package/main/plugins/plugin-base.js +9 -2
  11. package/main/plugins/provider-bundle.js +146 -0
  12. package/main/plugins/provider.js +170 -0
  13. package/main/plugins/react-query.js +1 -1
  14. package/main/plugins/recoil.js +1 -1
  15. package/main/plugins/types.js +1 -1
  16. package/main/utils/files.js +77 -0
  17. package/main/utils/schemas.js +1 -2
  18. package/main/utils/unused.js +68 -0
  19. package/module/builder/builder.js +28 -2
  20. package/module/generators/create-helpers.js +25 -0
  21. package/module/helpers/contractContextBase.js +92 -0
  22. package/module/helpers/contractsContextTSX.js +73 -0
  23. package/module/helpers/index.js +2 -0
  24. package/module/plugins/client.js +13 -10
  25. package/module/plugins/message-composer.js +12 -10
  26. package/module/plugins/msg-builder.js +1 -1
  27. package/module/plugins/plugin-base.js +15 -8
  28. package/module/plugins/provider-bundle.js +65 -0
  29. package/module/plugins/provider.js +81 -0
  30. package/module/plugins/react-query.js +1 -1
  31. package/module/plugins/recoil.js +1 -1
  32. package/module/plugins/types.js +1 -1
  33. package/module/utils/files.js +44 -0
  34. package/module/utils/schemas.js +1 -2
  35. package/module/utils/unused.js +45 -0
  36. package/package.json +3 -3
  37. package/src/builder/builder.ts +36 -1
  38. package/src/generators/create-helpers.ts +28 -0
  39. package/src/helpers/contractContextBase.ts +92 -0
  40. package/src/helpers/contractsContextTSX.ts +73 -0
  41. package/src/helpers/index.ts +2 -0
  42. package/src/plugins/client.ts +30 -14
  43. package/src/plugins/message-composer.ts +23 -14
  44. package/src/plugins/msg-builder.ts +1 -1
  45. package/src/plugins/plugin-base.ts +30 -20
  46. package/src/plugins/provider-bundle.ts +97 -0
  47. package/src/plugins/provider.ts +114 -0
  48. package/src/plugins/react-query.ts +1 -1
  49. package/src/plugins/recoil.ts +1 -1
  50. package/src/plugins/types.ts +1 -1
  51. package/src/utils/files.ts +73 -0
  52. package/src/utils/schemas.ts +1 -3
  53. package/src/utils/unused.ts +52 -0
  54. package/types/src/builder/builder.d.ts +7 -1
  55. package/types/src/generators/create-helpers.d.ts +3 -0
  56. package/types/src/helpers/contractContextBase.d.ts +1 -0
  57. package/types/src/helpers/contractsContextTSX.d.ts +1 -0
  58. package/types/src/helpers/index.d.ts +2 -0
  59. package/types/src/plugins/client.d.ts +4 -3
  60. package/types/src/plugins/message-composer.d.ts +4 -3
  61. package/types/src/plugins/plugin-base.d.ts +7 -3
  62. package/types/src/plugins/provider-bundle.d.ts +13 -0
  63. package/types/src/plugins/provider.d.ts +15 -0
  64. package/types/src/plugins/use-contracts.d.ts +12 -0
  65. package/types/src/utils/files.d.ts +3 -0
  66. package/types/src/utils/unused.d.ts +5 -0
@@ -1,11 +1,12 @@
1
- import { pascal } from 'case';
2
- import * as w from 'wasm-ast-types';
3
- import { findExecuteMsg, findAndParseTypes, findQueryMsg } from '../utils';
4
- import { RenderContext, getMessageProperties } from 'wasm-ast-types';
5
- import { BuilderPluginBase } from './plugin-base';
1
+ import { pascal } from "case";
2
+ import * as w from "wasm-ast-types";
3
+ import { findExecuteMsg, findAndParseTypes, findQueryMsg } from "../utils";
4
+ import { RenderContext, getMessageProperties } from "wasm-ast-types";
5
+ import { BuilderPluginBase } from "./plugin-base";
6
+ export const TYPE = "client";
6
7
  export class ClientPlugin extends BuilderPluginBase {
7
8
  initContext(contract, options) {
8
- return new RenderContext(contract, options);
9
+ return new RenderContext(contract, options, this.builder.builderContext);
9
10
  }
10
11
 
11
12
  async doRender(name, context) {
@@ -20,8 +21,8 @@ export class ClientPlugin extends BuilderPluginBase {
20
21
  const {
21
22
  schemas
22
23
  } = context.contract;
23
- const localname = pascal(name) + '.client.ts';
24
- const TypesFile = pascal(name) + '.types';
24
+ const localname = pascal(name) + ".client.ts";
25
+ const TypesFile = pascal(name) + ".types";
25
26
  const QueryMsg = findQueryMsg(schemas);
26
27
  const ExecuteMsg = findExecuteMsg(schemas);
27
28
  const typeHash = await findAndParseTypes(schemas);
@@ -37,6 +38,7 @@ export class ClientPlugin extends BuilderPluginBase {
37
38
  ReadOnlyInstance = pascal(`${name}ReadOnlyInterface`);
38
39
  body.push(w.createQueryInterface(context, ReadOnlyInstance, QueryMsg));
39
40
  body.push(w.createQueryClass(context, QueryClient, ReadOnlyInstance, QueryMsg));
41
+ context.addProviderInfo(name, w.PROVIDER_TYPES.QUERY_CLIENT_TYPE, QueryClient, localname);
40
42
  } // execute messages
41
43
 
42
44
 
@@ -48,16 +50,17 @@ export class ClientPlugin extends BuilderPluginBase {
48
50
  Instance = pascal(`${name}Interface`);
49
51
  body.push(w.createExecuteInterface(context, Instance, this.option.client.execExtendsQuery ? ReadOnlyInstance : null, ExecuteMsg));
50
52
  body.push(w.createExecuteClass(context, Client, Instance, this.option.client.execExtendsQuery ? QueryClient : null, ExecuteMsg));
53
+ context.addProviderInfo(name, w.PROVIDER_TYPES.SIGNING_CLIENT_TYPE, Client, localname);
51
54
  }
52
55
  }
53
56
 
54
- if (typeHash.hasOwnProperty('Coin')) {
57
+ if (typeHash.hasOwnProperty("Coin")) {
55
58
  // @ts-ignore
56
59
  delete context.utils.Coin;
57
60
  }
58
61
 
59
62
  return [{
60
- type: 'client',
63
+ type: TYPE,
61
64
  localname,
62
65
  body
63
66
  }];
@@ -1,11 +1,12 @@
1
- import { pascal } from 'case';
2
- import * as w from 'wasm-ast-types';
3
- import { findAndParseTypes, findExecuteMsg } from '../utils';
4
- import { getMessageProperties, RenderContext } from 'wasm-ast-types';
5
- import { BuilderPluginBase } from './plugin-base';
1
+ import { pascal } from "case";
2
+ import * as w from "wasm-ast-types";
3
+ import { findAndParseTypes, findExecuteMsg } from "../utils";
4
+ import { getMessageProperties, RenderContext } from "wasm-ast-types";
5
+ import { BuilderPluginBase } from "./plugin-base";
6
+ export const TYPE = "message-composer";
6
7
  export class MessageComposerPlugin extends BuilderPluginBase {
7
8
  initContext(contract, options) {
8
- return new RenderContext(contract, options);
9
+ return new RenderContext(contract, options, this.builder.builderContext);
9
10
  }
10
11
 
11
12
  async doRender(name, context) {
@@ -20,8 +21,8 @@ export class MessageComposerPlugin extends BuilderPluginBase {
20
21
  const {
21
22
  schemas
22
23
  } = context.contract;
23
- const localname = pascal(name) + '.message-composer.ts';
24
- const TypesFile = pascal(name) + '.types';
24
+ const localname = pascal(name) + ".message-composer.ts";
25
+ const TypesFile = pascal(name) + ".types";
25
26
  const ExecuteMsg = findExecuteMsg(schemas);
26
27
  const typeHash = await findAndParseTypes(schemas);
27
28
  const body = [];
@@ -35,16 +36,17 @@ export class MessageComposerPlugin extends BuilderPluginBase {
35
36
  const Interface = pascal(`${name}Message`);
36
37
  body.push(w.createMessageComposerInterface(context, Interface, ExecuteMsg));
37
38
  body.push(w.createMessageComposerClass(context, TheClass, Interface, ExecuteMsg));
39
+ context.addProviderInfo(name, w.PROVIDER_TYPES.MESSAGE_COMPOSER_TYPE, TheClass, localname);
38
40
  }
39
41
  }
40
42
 
41
- if (typeHash.hasOwnProperty('Coin')) {
43
+ if (typeHash.hasOwnProperty("Coin")) {
42
44
  // @ts-ignore
43
45
  delete context.utils.Coin;
44
46
  }
45
47
 
46
48
  return [{
47
- type: 'message-composer',
49
+ type: TYPE,
48
50
  localname,
49
51
  body
50
52
  }];
@@ -5,7 +5,7 @@ import { getMessageProperties, RenderContext } from 'wasm-ast-types';
5
5
  import { BuilderPluginBase } from './plugin-base';
6
6
  export class MsgBuilderPlugin extends BuilderPluginBase {
7
7
  initContext(contract, options) {
8
- return new RenderContext(contract, options);
8
+ return new RenderContext(contract, options, this.builder.builderContext);
9
9
  }
10
10
 
11
11
  async doRender(name, context) {
@@ -1,21 +1,28 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
- import { sync as mkdirp } from 'mkdirp';
3
- import { join } from 'path';
4
- import { writeFileSync } from 'fs';
5
- import { header } from '../utils/header';
6
- import generate from '@babel/generator';
7
- import * as t from '@babel/types';
2
+ import { sync as mkdirp } from "mkdirp";
3
+ import { join } from "path";
4
+ import { writeFileSync } from "fs";
5
+ import { header } from "../utils/header";
6
+ import generate from "@babel/generator";
7
+ import * as t from "@babel/types";
8
8
 
9
9
  /**
10
10
  * BuilderPluginBase enable ts-codegen users implement their own plugins by only implement a few functions.
11
11
  */
12
12
  export class BuilderPluginBase {
13
- constructor(opt) {
13
+ constructor(opt, builder) {
14
+ _defineProperty(this, "builder", void 0);
15
+
14
16
  _defineProperty(this, "option", void 0);
15
17
 
16
18
  _defineProperty(this, "utils", void 0);
17
19
 
18
20
  this.option = opt;
21
+ this.builder = builder;
22
+ }
23
+
24
+ setBuilder(builder) {
25
+ this.builder = builder;
19
26
  }
20
27
 
21
28
  async render(name, contractInfo, outPath) {
@@ -35,7 +42,7 @@ export class BuilderPluginBase {
35
42
  }
36
43
 
37
44
  return results.map(result => {
38
- const imports = context.getImports(this.utils);
45
+ const imports = context.getImports(this.utils, result.localname);
39
46
  const code = header + generate(t.program([...imports, ...result.body])).code;
40
47
  mkdirp(outPath);
41
48
  const filename = join(outPath, result.localname);
@@ -0,0 +1,65 @@
1
+ import { pascal } from "case";
2
+ import * as w from "wasm-ast-types";
3
+ import { RenderContext } from "wasm-ast-types";
4
+ import { BuilderPluginBase } from "./plugin-base";
5
+ import { GetLocalBaseNameByContractName } from "./provider";
6
+ export class ContractsProviderBundlePlugin extends BuilderPluginBase {
7
+ constructor(opt) {
8
+ super(opt);
9
+ this.utils = {
10
+ CosmWasmClient: "@cosmjs/cosmwasm-stargate",
11
+ SigningCosmWasmClient: "@cosmjs/cosmwasm-stargate",
12
+ IQueryClientProvider: "__contractContextBase__",
13
+ ISigningClientProvider: "__contractContextBase__",
14
+ IMessageComposerProvider: "__contractContextBase__"
15
+ };
16
+ }
17
+
18
+ initContext(contract, options) {
19
+ return new RenderContext(contract, options, this.builder.builderContext);
20
+ }
21
+
22
+ async doRender(name, context) {
23
+ if (!this.option?.useContracts?.enabled) {
24
+ return;
25
+ }
26
+
27
+ const providerInfos = context.getProviderInfos();
28
+
29
+ if (!Object.keys(providerInfos)?.length) {
30
+ return;
31
+ }
32
+
33
+ const localname = "contractContextProviders.ts";
34
+ const body = [];
35
+ context.addUtil("CosmWasmClient");
36
+ context.addUtil("SigningCosmWasmClient");
37
+ context.addUtil("IQueryClientProvider");
38
+ context.addUtil("ISigningClientProvider");
39
+ context.addUtil("IMessageComposerProvider");
40
+
41
+ for (const name in providerInfos) {
42
+ if (Object.prototype.hasOwnProperty.call(providerInfos, name)) {
43
+ const providerInfo = providerInfos[name];
44
+
45
+ for (const key in providerInfo) {
46
+ if (Object.prototype.hasOwnProperty.call(providerInfo, key)) {
47
+ const info = providerInfo[key];
48
+ body.push(w.importStmt([info.classname], `./${info.basename}`));
49
+ }
50
+ }
51
+
52
+ body.push(w.importStmt([pascal(name)], `./${GetLocalBaseNameByContractName(name)}`));
53
+ }
54
+ }
55
+
56
+ body.push(w.createIContractsContext(providerInfos));
57
+ body.push(w.createGettingProviders(providerInfos));
58
+ return [{
59
+ type: "plugin",
60
+ localname,
61
+ body
62
+ }];
63
+ }
64
+
65
+ }
@@ -0,0 +1,81 @@
1
+ import { pascal } from "case";
2
+ import * as w from "wasm-ast-types";
3
+ import { RenderContext } from "wasm-ast-types";
4
+ import { BuilderPluginBase } from "./plugin-base";
5
+ export const GetLocalNameByContractName = name => `${pascal(name)}.provider.ts`;
6
+ export const GetLocalBaseNameByContractName = name => `${pascal(name)}.provider`;
7
+ export class ContractsContextProviderPlugin extends BuilderPluginBase {
8
+ constructor(opt) {
9
+ super(opt);
10
+ this.utils = {
11
+ ContractBase: "__contractContextBase__",
12
+ IContractConstructor: "__contractContextBase__",
13
+ IEmptyClient: "__contractContextBase__"
14
+ };
15
+ }
16
+
17
+ initContext(contract, options) {
18
+ return new RenderContext(contract, options, this.builder.builderContext);
19
+ }
20
+
21
+ async doRender(name, context) {
22
+ if (!this.option?.useContracts?.enabled) {
23
+ return;
24
+ }
25
+
26
+ const providerInfo = context.getProviderInfos()[name];
27
+
28
+ if (!Object.keys(providerInfo)?.length) {
29
+ return;
30
+ }
31
+
32
+ context.addUtil("ContractBase");
33
+ context.addUtil("IContractConstructor");
34
+ const localname = GetLocalNameByContractName(name);
35
+ let needEmptyClientType = false;
36
+ let clientFile = null;
37
+ let clientClasses = [];
38
+ const body = [];
39
+ const signClientProviderInfo = providerInfo[w.PROVIDER_TYPES.SIGNING_CLIENT_TYPE];
40
+
41
+ if (signClientProviderInfo) {
42
+ clientFile = `./${signClientProviderInfo.basename}`;
43
+ clientClasses.push(signClientProviderInfo.classname);
44
+ } else {
45
+ needEmptyClientType = true;
46
+ }
47
+
48
+ const queryClientProviderInfo = providerInfo[w.PROVIDER_TYPES.QUERY_CLIENT_TYPE];
49
+
50
+ if (queryClientProviderInfo) {
51
+ clientFile = `./${queryClientProviderInfo.basename}`;
52
+ clientClasses.push(queryClientProviderInfo.classname);
53
+ } else {
54
+ needEmptyClientType = true;
55
+ }
56
+
57
+ if (clientFile) {
58
+ body.push(w.importStmt(clientClasses, clientFile));
59
+ }
60
+
61
+ const messageComposerProviderInfo = providerInfo[w.PROVIDER_TYPES.MESSAGE_COMPOSER_TYPE];
62
+
63
+ if (messageComposerProviderInfo) {
64
+ body.push(w.importStmt([messageComposerProviderInfo.classname], `./${messageComposerProviderInfo.basename}`));
65
+ } else {
66
+ needEmptyClientType = true;
67
+ }
68
+
69
+ if (needEmptyClientType) {
70
+ context.addUtil("IEmptyClient");
71
+ }
72
+
73
+ body.push(w.createProvider(name, providerInfo));
74
+ return [{
75
+ type: "plugin",
76
+ localname,
77
+ body
78
+ }];
79
+ }
80
+
81
+ }
@@ -5,7 +5,7 @@ import { getMessageProperties, RenderContext } from 'wasm-ast-types';
5
5
  import { BuilderPluginBase } from './plugin-base';
6
6
  export class ReactQueryPlugin extends BuilderPluginBase {
7
7
  initContext(contract, options) {
8
- return new RenderContext(contract, options);
8
+ return new RenderContext(contract, options, this.builder.builderContext);
9
9
  }
10
10
 
11
11
  async doRender(name, context) {
@@ -14,7 +14,7 @@ export class RecoilPlugin extends BuilderPluginBase {
14
14
  }
15
15
 
16
16
  initContext(contract, options) {
17
- return new RenderContext(contract, options);
17
+ return new RenderContext(contract, options, this.builder.builderContext);
18
18
  }
19
19
 
20
20
  async doRender(name, context) {
@@ -6,7 +6,7 @@ import { RenderContext } from 'wasm-ast-types';
6
6
  import { BuilderPluginBase } from './plugin-base';
7
7
  export class TypesPlugin extends BuilderPluginBase {
8
8
  initContext(contract, options) {
9
- return new RenderContext(contract, options);
9
+ return new RenderContext(contract, options, this.builder.builderContext);
10
10
  }
11
11
 
12
12
  async doRender(name, context) {
@@ -0,0 +1,44 @@
1
+ import * as t from "@babel/types";
2
+ import { parse } from "@babel/parser";
3
+ import { sync as mkdirp } from "mkdirp";
4
+ import { writeFileSync } from "fs";
5
+ import { dirname } from "path";
6
+ import generate from "@babel/generator";
7
+ import { unused } from "./unused";
8
+ import traverse from "@babel/traverse";
9
+ export const writeAstToFile = (outPath, program, filename, removeUnusedImports = false, isTsDisable = false, isEslintDisable = false) => {
10
+ const ast = t.program(program);
11
+ const content = generate(ast).code;
12
+
13
+ if (removeUnusedImports) {
14
+ const plugins = ["typescript"];
15
+ const newAst = parse(content, {
16
+ sourceType: "module",
17
+ plugins
18
+ });
19
+ traverse(newAst, unused);
20
+ const content2 = generate(newAst).code;
21
+ writeContentToFile(outPath, content2, filename, isTsDisable, isEslintDisable);
22
+ } else {
23
+ writeContentToFile(outPath, content, filename, isTsDisable, isEslintDisable);
24
+ }
25
+ };
26
+ export const writeContentToFile = (outPath, content, filename, isTsDisable = false, isEslintDisable = false) => {
27
+ let esLintPrefix = "";
28
+ let tsLintPrefix = "";
29
+ let nameWithoutPath = filename.replace(outPath, ""); // strip off leading slash
30
+
31
+ if (nameWithoutPath.startsWith("/")) nameWithoutPath = nameWithoutPath.replace(/^\//, "");
32
+
33
+ if (isTsDisable) {
34
+ tsLintPrefix = `//@ts-nocheck\n`;
35
+ }
36
+
37
+ if (isEslintDisable) {
38
+ esLintPrefix = `/* eslint-disable */\n`;
39
+ }
40
+
41
+ const text = tsLintPrefix + esLintPrefix + content;
42
+ mkdirp(dirname(filename));
43
+ writeFileSync(filename, text);
44
+ };
@@ -72,8 +72,7 @@ export const findQueryMsg = schemas => {
72
72
  return QueryMsg;
73
73
  };
74
74
  export const findExecuteMsg = schemas => {
75
- const ExecuteMsg = schemas.find(schema => schema.title === 'ExecuteMsg' || schema.title === 'ExecuteMsg_for_Empty' || // if cleanse is used, this is never
76
- schema.title === 'ExecuteMsgForEmpty');
75
+ const ExecuteMsg = schemas.find(schema => schema.title.startsWith('ExecuteMsg'));
77
76
  return ExecuteMsg;
78
77
  };
79
78
  export const findAndParseTypes = async schemas => {
@@ -0,0 +1,45 @@
1
+ //@ts-nocheck
2
+ import * as t from '@babel/types'; // https://github.com/chuyik/babel-plugin-danger-remove-unused-import
3
+ // https://github.com/chuyik/babel-plugin-danger-remove-unused-import/blob/c5454c21e94698a2464a12baa5590761932a71a8/License#L1
4
+
5
+ export const unused = {
6
+ Program: {
7
+ exit: path => {
8
+ const UnRefBindings = new Map();
9
+
10
+ for (const [name, binding] of Object.entries(path.scope.bindings)) {
11
+ if (!binding.path.parentPath || binding.kind !== 'module') continue;
12
+ const source = binding.path.parentPath.get('source');
13
+ const importName = source.node.value;
14
+ if (!t.isStringLiteral(source)) continue;
15
+ const key = `${importName}(${source.node.loc && source.node.loc.start.line})`;
16
+
17
+ if (!UnRefBindings.has(key)) {
18
+ UnRefBindings.set(key, binding);
19
+ }
20
+
21
+ if (binding.referenced) {
22
+ UnRefBindings.set(key, null);
23
+ } else {
24
+ const nodeType = binding.path.node.type;
25
+
26
+ if (nodeType === 'ImportSpecifier') {
27
+ binding.path.remove();
28
+ } else if (nodeType === 'ImportDefaultSpecifier') {
29
+ binding.path.remove();
30
+ } else if (nodeType === 'ImportNamespaceSpecifier') {
31
+ binding.path.remove();
32
+ } else if (binding.path.parentPath) {
33
+ binding.path.parentPath.remove();
34
+ }
35
+ }
36
+ }
37
+
38
+ UnRefBindings.forEach((binding, key) => {
39
+ if (binding && binding.path.parentPath) {
40
+ binding.path.parentPath.remove();
41
+ }
42
+ });
43
+ }
44
+ }
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmwasm/ts-codegen",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "@cosmwasm/ts-codegen converts your CosmWasm smart contracts into dev-friendly TypeScript classes so you can focus on shipping code.",
5
5
  "author": "Dan Lynch <pyramation@gmail.com>",
6
6
  "homepage": "https://github.com/cosmwasm/ts-codegen",
@@ -96,7 +96,7 @@
96
96
  "parse-package-name": "1.0.0",
97
97
  "rimraf": "3.0.2",
98
98
  "shelljs": "0.8.5",
99
- "wasm-ast-types": "^0.23.0"
99
+ "wasm-ast-types": "^0.24.0"
100
100
  },
101
- "gitHead": "9979ae4b7209f920fe1b822ede2ba80ee0f2eade"
101
+ "gitHead": "4553eb80cd89969583df6164d1c764ab5b289c63"
102
102
  }
@@ -1,4 +1,4 @@
1
- import { RenderOptions, defaultOptions, RenderContext, ContractInfo, MessageComposerOptions} from "wasm-ast-types";
1
+ import { RenderOptions, defaultOptions, RenderContext, ContractInfo, MessageComposerOptions, BuilderContext} from "wasm-ast-types";
2
2
 
3
3
  import { header } from '../utils/header';
4
4
  import { join } from "path";
@@ -21,6 +21,9 @@ import { MsgBuilderPlugin } from "../plugins/msg-builder";
21
21
  import { MessageComposerPlugin } from "../plugins/message-composer";
22
22
  import { ClientPlugin } from "../plugins/client";
23
23
  import { TypesPlugin } from "../plugins/types";
24
+ import { ContractsContextProviderPlugin } from "../plugins/provider";
25
+ import { createHelpers } from "../generators/create-helpers";
26
+ import { ContractsProviderBundlePlugin } from "../plugins/provider-bundle";
24
27
 
25
28
  const defaultOpts: TSBuilderOptions = {
26
29
  bundle: {
@@ -44,8 +47,14 @@ export interface BundleOptions {
44
47
  bundlePath?: string;
45
48
  };
46
49
 
50
+ export interface UseContractsOptions {
51
+ enabled?: boolean;
52
+ filename?: string;
53
+ };
54
+
47
55
  export type TSBuilderOptions = {
48
56
  bundle?: BundleOptions;
57
+ useContracts?: UseContractsOptions;
49
58
  } & RenderOptions;
50
59
 
51
60
  export type BuilderFileType = 'type' | 'client' | 'recoil' | 'react-query' | 'message-composer' | 'msg-builder' | 'plugin';
@@ -83,6 +92,7 @@ export class TSBuilder {
83
92
  outPath: string;
84
93
  options?: TSBuilderOptions;
85
94
  plugins: IBuilderPlugin[] = [];
95
+ builderContext: BuilderContext = new BuilderContext();
86
96
 
87
97
  protected files: BuilderFile[] = [];
88
98
 
@@ -94,6 +104,7 @@ export class TSBuilder {
94
104
  new ReactQueryPlugin(this.options),
95
105
  new RecoilPlugin(this.options),
96
106
  new MsgBuilderPlugin(this.options),
107
+ new ContractsContextProviderPlugin(this.options),
97
108
  ]);
98
109
  }
99
110
 
@@ -113,6 +124,8 @@ export class TSBuilder {
113
124
  if (plugins && plugins.length) {
114
125
  [].push.apply(this.plugins, plugins);
115
126
  }
127
+
128
+ this.plugins.forEach(plugin=> plugin.setBuilder(this))
116
129
  }
117
130
 
118
131
  async build() {
@@ -147,6 +160,28 @@ export class TSBuilder {
147
160
  if (this.options.bundle.enabled) {
148
161
  this.bundle();
149
162
  }
163
+
164
+ //create useContracts bundle file
165
+ const contractsProviderBundlePlugin = new ContractsProviderBundlePlugin(this.options);
166
+ contractsProviderBundlePlugin.setBuilder(this);
167
+
168
+ let files = await contractsProviderBundlePlugin.render(
169
+ "",
170
+ {
171
+ schemas: [],
172
+ },
173
+ this.outPath
174
+ );
175
+ if(files && files.length){
176
+ [].push.apply(this.files, files);
177
+ }
178
+
179
+ createHelpers({
180
+ outPath: this.outPath,
181
+ contracts: this.contracts,
182
+ options: this.options,
183
+ plugins: this.plugins,
184
+ }, this.builderContext);
150
185
  }
151
186
 
152
187
  async bundle() {
@@ -0,0 +1,28 @@
1
+ import { join, dirname } from "path";
2
+ import { sync as mkdirp } from "mkdirp";
3
+ import pkg from "../../package.json";
4
+ import { writeContentToFile } from "../utils/files";
5
+ import { TSBuilderInput } from "../builder";
6
+ import { contractContextBase, contractsContextTSX } from "../helpers";
7
+ import { BuilderContext } from "wasm-ast-types";
8
+
9
+ const version = process.env.NODE_ENV === "test" ? "latest" : pkg.version;
10
+ const header = `/**
11
+ * This file and any referenced files were automatically generated by ${pkg.name}@${version}
12
+ * DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
13
+ * and run the transpile command or yarn proto command to regenerate this bundle.
14
+ */
15
+ \n`;
16
+
17
+ const write = (outPath: string, file: string, content: string) => {
18
+ const outFile = join(outPath, file);
19
+ mkdirp(dirname(outFile));
20
+ writeContentToFile(outPath, header + content, outFile);
21
+ };
22
+
23
+ export const createHelpers = (input: TSBuilderInput, builderContext: BuilderContext) => {
24
+ if (input.options?.useContracts?.enabled && Object.keys(builderContext.providers)?.length) {
25
+ write(input.outPath, "contractContextBase.ts", contractContextBase);
26
+ write(input.outPath, "contracts-context.tsx", contractsContextTSX);
27
+ }
28
+ };
@@ -0,0 +1,92 @@
1
+ export const contractContextBase = `
2
+ import {
3
+ CosmWasmClient,
4
+ SigningCosmWasmClient,
5
+ } from '@cosmjs/cosmwasm-stargate';
6
+
7
+ export interface IContractConstructor {
8
+ address: string | undefined;
9
+ cosmWasmClient: CosmWasmClient | undefined;
10
+ signingCosmWasmClient: SigningCosmWasmClient | undefined;
11
+ }
12
+
13
+ export const NO_SINGING_ERROR_MESSAGE = 'signingCosmWasmClient not connected';
14
+
15
+ export const NO_COSMWASW_CLIENT_ERROR_MESSAGE = 'cosmWasmClient not connected';
16
+
17
+ export const NO_ADDRESS_ERROR_MESSAGE = "address doesn't exist";
18
+
19
+ export const NO_SIGNING_CLIENT_ERROR_MESSAGE =
20
+ 'Signing client is not generated. Please check ts-codegen config';
21
+
22
+ export const NO_QUERY_CLIENT_ERROR_MESSAGE =
23
+ 'Query client is not generated. Please check ts-codegen config';
24
+
25
+ export const NO_MESSAGE_COMPOSER_ERROR_MESSAGE =
26
+ 'Message composer client is not generated. Please check ts-codegen config';
27
+
28
+ /**
29
+ * a placeholder for non-generated classes
30
+ */
31
+ export interface IEmptyClient {}
32
+
33
+ export interface ISigningClientProvider<T> {
34
+ getSigningClient(contractAddr: string): T;
35
+ }
36
+
37
+ export interface IQueryClientProvider<T> {
38
+ getQueryClient(contractAddr: string): T;
39
+ }
40
+
41
+ export interface IMessageComposerProvider<T> {
42
+ getMessageComposer(contractAddr: string): T;
43
+ }
44
+
45
+ export class ContractBase<
46
+ TSign = IEmptyClient,
47
+ TQuery = IEmptyClient,
48
+ TMsgComposer = IEmptyClient
49
+ > {
50
+ constructor(
51
+ protected address: string | undefined,
52
+ protected cosmWasmClient: CosmWasmClient | undefined,
53
+ protected signingCosmWasmClient: SigningCosmWasmClient | undefined,
54
+ private TSign?: new (
55
+ client: SigningCosmWasmClient,
56
+ sender: string,
57
+ contractAddress: string
58
+ ) => TSign,
59
+ private TQuery?: new (
60
+ client: CosmWasmClient,
61
+ contractAddress: string
62
+ ) => TQuery,
63
+ private TMsgComposer?: new (
64
+ sender: string,
65
+ contractAddress: string
66
+ ) => TMsgComposer
67
+ ) {}
68
+
69
+ public getSigningClient(contractAddr: string): TSign {
70
+ if (!this.signingCosmWasmClient) throw new Error(NO_SINGING_ERROR_MESSAGE);
71
+ if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);
72
+ if (!this.TSign) throw new Error(NO_SIGNING_CLIENT_ERROR_MESSAGE);
73
+ return new this.TSign(
74
+ this.signingCosmWasmClient,
75
+ this.address,
76
+ contractAddr
77
+ );
78
+ }
79
+
80
+ public getQueryClient(contractAddr: string): TQuery {
81
+ if (!this.cosmWasmClient) throw new Error(NO_COSMWASW_CLIENT_ERROR_MESSAGE);
82
+ if (!this.TQuery) throw new Error(NO_QUERY_CLIENT_ERROR_MESSAGE);
83
+ return new this.TQuery(this.cosmWasmClient, contractAddr);
84
+ }
85
+
86
+ public getMessageComposer(contractAddr: string): TMsgComposer {
87
+ if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);
88
+ if (!this.TMsgComposer) throw new Error(NO_MESSAGE_COMPOSER_ERROR_MESSAGE);
89
+ return new this.TMsgComposer(this.address, contractAddr);
90
+ }
91
+ }
92
+ `;