@dedot/codegen 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/chaintypes/generator/IndexGen.d.ts +3 -1
  2. package/chaintypes/generator/IndexGen.js +9 -3
  3. package/chaintypes/generator/JsonRpcGen.js +4 -2
  4. package/chaintypes/generator/TypesGen.js +2 -3
  5. package/chaintypes/index.d.ts +2 -1
  6. package/chaintypes/index.js +9 -10
  7. package/chaintypes/templates/index.hbs +1 -1
  8. package/chaintypes/templates/json-rpc.hbs +6 -1
  9. package/cjs/chaintypes/generator/IndexGen.js +8 -2
  10. package/cjs/chaintypes/generator/JsonRpcGen.js +4 -2
  11. package/cjs/chaintypes/generator/TypesGen.js +2 -3
  12. package/cjs/chaintypes/index.js +9 -10
  13. package/cjs/chaintypes/templates/index.hbs +1 -1
  14. package/cjs/chaintypes/templates/json-rpc.hbs +6 -1
  15. package/cjs/shared/BaseTypesGen.js +21 -10
  16. package/cjs/typink/generator/ConstructorQueryGen.js +2 -3
  17. package/cjs/typink/generator/ConstructorTxGen.js +3 -5
  18. package/cjs/typink/generator/EventsGen.js +0 -1
  19. package/cjs/typink/generator/IndexGen.js +9 -1
  20. package/cjs/typink/generator/QueryGen.js +8 -31
  21. package/cjs/typink/generator/TxGen.js +3 -5
  22. package/cjs/typink/generator/TypesGen.js +74 -4
  23. package/cjs/typink/index.js +1 -1
  24. package/cjs/typink/templates/index.hbs +1 -1
  25. package/package.json +10 -10
  26. package/shared/BaseTypesGen.d.ts +4 -2
  27. package/shared/BaseTypesGen.js +21 -10
  28. package/types.d.ts +1 -0
  29. package/typink/generator/ConstructorQueryGen.d.ts +1 -1
  30. package/typink/generator/ConstructorQueryGen.js +2 -3
  31. package/typink/generator/ConstructorTxGen.d.ts +1 -1
  32. package/typink/generator/ConstructorTxGen.js +3 -5
  33. package/typink/generator/EventsGen.js +0 -1
  34. package/typink/generator/IndexGen.js +10 -2
  35. package/typink/generator/QueryGen.d.ts +1 -2
  36. package/typink/generator/QueryGen.js +9 -32
  37. package/typink/generator/TxGen.d.ts +1 -1
  38. package/typink/generator/TxGen.js +3 -5
  39. package/typink/generator/TypesGen.d.ts +3 -0
  40. package/typink/generator/TypesGen.js +75 -5
  41. package/typink/index.js +1 -1
  42. package/typink/templates/index.hbs +1 -1
@@ -1,5 +1,7 @@
1
+ import { SubstrateRuntimeVersion } from '@dedot/api';
1
2
  export declare class IndexGen {
2
3
  readonly interfaceName: string;
3
- constructor(interfaceName: string);
4
+ readonly runtimeVersion: SubstrateRuntimeVersion;
5
+ constructor(interfaceName: string, runtimeVersion: SubstrateRuntimeVersion);
4
6
  generate(useSubPaths?: boolean): Promise<string>;
5
7
  }
@@ -1,16 +1,22 @@
1
1
  import { TypeImports } from '../../shared/index.js';
2
- import { beautifySourceCode, compileTemplate } from '../../utils.js';
2
+ import { beautifySourceCode, commentBlock, compileTemplate } from '../../utils.js';
3
3
  export class IndexGen {
4
4
  interfaceName;
5
- constructor(interfaceName) {
5
+ runtimeVersion;
6
+ constructor(interfaceName, runtimeVersion) {
6
7
  this.interfaceName = interfaceName;
8
+ this.runtimeVersion = runtimeVersion;
7
9
  }
8
10
  async generate(useSubPaths = false) {
9
11
  const interfaceName = this.interfaceName;
10
12
  const typeImports = new TypeImports();
11
13
  typeImports.addKnownType('GenericSubstrateApi', 'RpcLegacy', 'RpcV2', 'RpcVersion');
12
14
  const importTypes = typeImports.toImports({ useSubPaths });
15
+ const interfaceDocs = commentBlock([
16
+ `@name: ${interfaceName}`, // prettier-end-here
17
+ `@specVersion: ${this.runtimeVersion.specVersion}`,
18
+ ]);
13
19
  const template = compileTemplate('chaintypes/templates/index.hbs');
14
- return beautifySourceCode(template({ importTypes, interfaceName }));
20
+ return beautifySourceCode(template({ interfaceName, interfaceDocs, importTypes }));
15
21
  }
16
22
  }
@@ -35,8 +35,10 @@ export class JsonRpcGen extends ApiGen {
35
35
  super(typesGen);
36
36
  this.typesGen = typesGen;
37
37
  this.rpcMethods = rpcMethods;
38
- HIDDEN_RPCS.filter((one) => !rpcMethods.includes(one)).forEach((one) => rpcMethods.push(one));
39
- rpcMethods.sort();
38
+ if (rpcMethods.length > 0) {
39
+ HIDDEN_RPCS.filter((one) => !rpcMethods.includes(one)).forEach((one) => rpcMethods.push(one));
40
+ rpcMethods.sort();
41
+ }
40
42
  }
41
43
  generate(useSubPaths = false) {
42
44
  this.typesGen.clearCache();
@@ -23,11 +23,10 @@ export class TypesGen extends BaseTypesGen {
23
23
  metadata;
24
24
  registry;
25
25
  constructor(metadata) {
26
- super(metadata.types);
26
+ super(metadata.types, SKIP_TYPES);
27
27
  this.metadata = metadata;
28
- this.skipTypes = SKIP_TYPES;
29
28
  this.registry = new PortableRegistry(this.metadata);
30
- this.includedTypes = this.includeTypes();
29
+ this.includedTypes = this.calculateIncludedTypes();
31
30
  }
32
31
  generate(useSubPaths = false) {
33
32
  this.clearCache();
@@ -1,4 +1,5 @@
1
+ import { SubstrateRuntimeVersion } from '@dedot/api';
1
2
  import { MetadataLatest } from '@dedot/codecs';
2
3
  import { GeneratedResult } from '../types.js';
3
4
  export declare function generateTypesFromEndpoint(chain: string, endpoint: string, outDir?: string, extension?: string, useSubPaths?: boolean): Promise<GeneratedResult>;
4
- export declare function generateTypes(chain: string, metadata: MetadataLatest, rpcMethods: string[], runtimeApis: Record<string, number>, outDir?: string, extension?: string, useSubPaths?: boolean): Promise<GeneratedResult>;
5
+ export declare function generateTypes(chain: string, metadata: MetadataLatest, rpcMethods: string[], runtimeVersion: SubstrateRuntimeVersion, outDir?: string, extension?: string, useSubPaths?: boolean): Promise<GeneratedResult>;
@@ -6,15 +6,14 @@ import * as path from 'path';
6
6
  import { ConstsGen, ErrorsGen, EventsGen, IndexGen, JsonRpcGen, QueryGen, RuntimeApisGen, TxGen, TypesGen, } from './generator/index.js';
7
7
  export async function generateTypesFromEndpoint(chain, endpoint, outDir, extension = 'd.ts', useSubPaths = false) {
8
8
  // Immediately throw error if cannot connect to provider for the first time.
9
- const api = await LegacyClient.new(new WsProvider({ endpoint, retryDelayMs: 0, timeout: 0 }));
10
- const { methods } = await api.rpc.rpc_methods();
11
- const apis = api.runtimeVersion.apis || {};
12
- chain = chain || api.runtimeVersion.specName || 'local';
13
- const result = await generateTypes(chain, api.metadata.latest, methods, apis, outDir, extension, useSubPaths);
14
- await api.disconnect();
9
+ const client = await LegacyClient.new(new WsProvider({ endpoint, retryDelayMs: 0, timeout: 0 }));
10
+ const { methods } = await client.rpc.rpc_methods();
11
+ chain = chain || client.runtimeVersion.specName || 'local';
12
+ const result = await generateTypes(chain, client.metadata.latest, methods, client.runtimeVersion, outDir, extension, useSubPaths);
13
+ await client.disconnect();
15
14
  return result;
16
15
  }
17
- export async function generateTypes(chain, metadata, rpcMethods, runtimeApis, outDir = '.', extension = 'd.ts', useSubPaths = false) {
16
+ export async function generateTypes(chain, metadata, rpcMethods, runtimeVersion, outDir = '.', extension = 'd.ts', useSubPaths = false) {
18
17
  const dirPath = path.resolve(outDir, stringDashCase(chain));
19
18
  const defTypesFileName = path.join(dirPath, `types.${extension}`);
20
19
  const constsTypesFileName = path.join(dirPath, `consts.${extension}`);
@@ -33,10 +32,10 @@ export async function generateTypes(chain, metadata, rpcMethods, runtimeApis, ou
33
32
  const constsGen = new ConstsGen(typesGen);
34
33
  const queryGen = new QueryGen(typesGen);
35
34
  const jsonRpcGen = new JsonRpcGen(typesGen, rpcMethods);
36
- const indexGen = new IndexGen(interfaceName);
35
+ const indexGen = new IndexGen(interfaceName, runtimeVersion);
37
36
  const errorsGen = new ErrorsGen(typesGen);
38
37
  const eventsGen = new EventsGen(typesGen);
39
- const runtimeApisGen = new RuntimeApisGen(typesGen, runtimeApis);
38
+ const runtimeApisGen = new RuntimeApisGen(typesGen, runtimeVersion.apis);
40
39
  const txGen = new TxGen(typesGen);
41
40
  fs.writeFileSync(defTypesFileName, await typesGen.generate(useSubPaths));
42
41
  fs.writeFileSync(errorsFileName, await errorsGen.generate(useSubPaths));
@@ -47,5 +46,5 @@ export async function generateTypes(chain, metadata, rpcMethods, runtimeApis, ou
47
46
  fs.writeFileSync(txFileName, await txGen.generate(useSubPaths));
48
47
  fs.writeFileSync(indexFileName, await indexGen.generate(useSubPaths));
49
48
  fs.writeFileSync(runtimeApisFileName, await runtimeApisGen.generate(useSubPaths));
50
- return { interfaceName };
49
+ return { interfaceName, outputFolder: dirPath };
51
50
  }
@@ -21,7 +21,7 @@ export interface Versioned{{{interfaceName}}}<Rv extends RpcVersion> extends Gen
21
21
  tx: ChainTx<Rv>;
22
22
  }
23
23
 
24
- export interface {{{interfaceName}}} {
24
+ {{interfaceDocs}}export interface {{{interfaceName}}} {
25
25
  legacy: Versioned{{{interfaceName}}}<RpcLegacy>;
26
26
  v2: Versioned{{{interfaceName}}}<RpcV2>;
27
27
  }
@@ -2,4 +2,9 @@
2
2
 
3
3
  {{{ importTypes }}}
4
4
 
5
- export type ChainJsonRpcApis<Rv extends RpcVersion> = Pick<JsonRpcApis, {{{jsonRpcMethods}}}> & GenericJsonRpcApis<Rv>;
5
+ export type ChainJsonRpcApis<Rv extends RpcVersion> =
6
+ {{#if jsonRpcMethods.length}}
7
+ Pick<JsonRpcApis, {{{jsonRpcMethods}}}> & GenericJsonRpcApis<Rv>;
8
+ {{else}}
9
+ JsonRpcApis & GenericJsonRpcApis<Rv>;
10
+ {{/if}}
@@ -5,16 +5,22 @@ const index_js_1 = require("../../shared/index.js");
5
5
  const utils_js_1 = require("../../utils.js");
6
6
  class IndexGen {
7
7
  interfaceName;
8
- constructor(interfaceName) {
8
+ runtimeVersion;
9
+ constructor(interfaceName, runtimeVersion) {
9
10
  this.interfaceName = interfaceName;
11
+ this.runtimeVersion = runtimeVersion;
10
12
  }
11
13
  async generate(useSubPaths = false) {
12
14
  const interfaceName = this.interfaceName;
13
15
  const typeImports = new index_js_1.TypeImports();
14
16
  typeImports.addKnownType('GenericSubstrateApi', 'RpcLegacy', 'RpcV2', 'RpcVersion');
15
17
  const importTypes = typeImports.toImports({ useSubPaths });
18
+ const interfaceDocs = (0, utils_js_1.commentBlock)([
19
+ `@name: ${interfaceName}`, // prettier-end-here
20
+ `@specVersion: ${this.runtimeVersion.specVersion}`,
21
+ ]);
16
22
  const template = (0, utils_js_1.compileTemplate)('chaintypes/templates/index.hbs');
17
- return (0, utils_js_1.beautifySourceCode)(template({ importTypes, interfaceName }));
23
+ return (0, utils_js_1.beautifySourceCode)(template({ interfaceName, interfaceDocs, importTypes }));
18
24
  }
19
25
  }
20
26
  exports.IndexGen = IndexGen;
@@ -38,8 +38,10 @@ class JsonRpcGen extends ApiGen_js_1.ApiGen {
38
38
  super(typesGen);
39
39
  this.typesGen = typesGen;
40
40
  this.rpcMethods = rpcMethods;
41
- HIDDEN_RPCS.filter((one) => !rpcMethods.includes(one)).forEach((one) => rpcMethods.push(one));
42
- rpcMethods.sort();
41
+ if (rpcMethods.length > 0) {
42
+ HIDDEN_RPCS.filter((one) => !rpcMethods.includes(one)).forEach((one) => rpcMethods.push(one));
43
+ rpcMethods.sort();
44
+ }
43
45
  }
44
46
  generate(useSubPaths = false) {
45
47
  this.typesGen.clearCache();
@@ -26,11 +26,10 @@ class TypesGen extends index_js_1.BaseTypesGen {
26
26
  metadata;
27
27
  registry;
28
28
  constructor(metadata) {
29
- super(metadata.types);
29
+ super(metadata.types, SKIP_TYPES);
30
30
  this.metadata = metadata;
31
- this.skipTypes = SKIP_TYPES;
32
31
  this.registry = new codecs_1.PortableRegistry(this.metadata);
33
- this.includedTypes = this.includeTypes();
32
+ this.includedTypes = this.calculateIncludedTypes();
34
33
  }
35
34
  generate(useSubPaths = false) {
36
35
  this.clearCache();
@@ -32,16 +32,15 @@ const path = __importStar(require("path"));
32
32
  const index_js_1 = require("./generator/index.js");
33
33
  async function generateTypesFromEndpoint(chain, endpoint, outDir, extension = 'd.ts', useSubPaths = false) {
34
34
  // Immediately throw error if cannot connect to provider for the first time.
35
- const api = await api_1.LegacyClient.new(new providers_1.WsProvider({ endpoint, retryDelayMs: 0, timeout: 0 }));
36
- const { methods } = await api.rpc.rpc_methods();
37
- const apis = api.runtimeVersion.apis || {};
38
- chain = chain || api.runtimeVersion.specName || 'local';
39
- const result = await generateTypes(chain, api.metadata.latest, methods, apis, outDir, extension, useSubPaths);
40
- await api.disconnect();
35
+ const client = await api_1.LegacyClient.new(new providers_1.WsProvider({ endpoint, retryDelayMs: 0, timeout: 0 }));
36
+ const { methods } = await client.rpc.rpc_methods();
37
+ chain = chain || client.runtimeVersion.specName || 'local';
38
+ const result = await generateTypes(chain, client.metadata.latest, methods, client.runtimeVersion, outDir, extension, useSubPaths);
39
+ await client.disconnect();
41
40
  return result;
42
41
  }
43
42
  exports.generateTypesFromEndpoint = generateTypesFromEndpoint;
44
- async function generateTypes(chain, metadata, rpcMethods, runtimeApis, outDir = '.', extension = 'd.ts', useSubPaths = false) {
43
+ async function generateTypes(chain, metadata, rpcMethods, runtimeVersion, outDir = '.', extension = 'd.ts', useSubPaths = false) {
45
44
  const dirPath = path.resolve(outDir, (0, utils_1.stringDashCase)(chain));
46
45
  const defTypesFileName = path.join(dirPath, `types.${extension}`);
47
46
  const constsTypesFileName = path.join(dirPath, `consts.${extension}`);
@@ -60,10 +59,10 @@ async function generateTypes(chain, metadata, rpcMethods, runtimeApis, outDir =
60
59
  const constsGen = new index_js_1.ConstsGen(typesGen);
61
60
  const queryGen = new index_js_1.QueryGen(typesGen);
62
61
  const jsonRpcGen = new index_js_1.JsonRpcGen(typesGen, rpcMethods);
63
- const indexGen = new index_js_1.IndexGen(interfaceName);
62
+ const indexGen = new index_js_1.IndexGen(interfaceName, runtimeVersion);
64
63
  const errorsGen = new index_js_1.ErrorsGen(typesGen);
65
64
  const eventsGen = new index_js_1.EventsGen(typesGen);
66
- const runtimeApisGen = new index_js_1.RuntimeApisGen(typesGen, runtimeApis);
65
+ const runtimeApisGen = new index_js_1.RuntimeApisGen(typesGen, runtimeVersion.apis);
67
66
  const txGen = new index_js_1.TxGen(typesGen);
68
67
  fs.writeFileSync(defTypesFileName, await typesGen.generate(useSubPaths));
69
68
  fs.writeFileSync(errorsFileName, await errorsGen.generate(useSubPaths));
@@ -74,6 +73,6 @@ async function generateTypes(chain, metadata, rpcMethods, runtimeApis, outDir =
74
73
  fs.writeFileSync(txFileName, await txGen.generate(useSubPaths));
75
74
  fs.writeFileSync(indexFileName, await indexGen.generate(useSubPaths));
76
75
  fs.writeFileSync(runtimeApisFileName, await runtimeApisGen.generate(useSubPaths));
77
- return { interfaceName };
76
+ return { interfaceName, outputFolder: dirPath };
78
77
  }
79
78
  exports.generateTypes = generateTypes;
@@ -21,7 +21,7 @@ export interface Versioned{{{interfaceName}}}<Rv extends RpcVersion> extends Gen
21
21
  tx: ChainTx<Rv>;
22
22
  }
23
23
 
24
- export interface {{{interfaceName}}} {
24
+ {{interfaceDocs}}export interface {{{interfaceName}}} {
25
25
  legacy: Versioned{{{interfaceName}}}<RpcLegacy>;
26
26
  v2: Versioned{{{interfaceName}}}<RpcV2>;
27
27
  }
@@ -2,4 +2,9 @@
2
2
 
3
3
  {{{ importTypes }}}
4
4
 
5
- export type ChainJsonRpcApis<Rv extends RpcVersion> = Pick<JsonRpcApis, {{{jsonRpcMethods}}}> & GenericJsonRpcApis<Rv>;
5
+ export type ChainJsonRpcApis<Rv extends RpcVersion> =
6
+ {{#if jsonRpcMethods.length}}
7
+ Pick<JsonRpcApis, {{{jsonRpcMethods}}}> & GenericJsonRpcApis<Rv>;
8
+ {{else}}
9
+ JsonRpcApis & GenericJsonRpcApis<Rv>;
10
+ {{/if}}
@@ -14,11 +14,13 @@ class BaseTypesGen {
14
14
  includedTypes;
15
15
  typeImports;
16
16
  skipTypes;
17
- constructor(types) {
17
+ knownTypes;
18
+ constructor(types, skipTypes = []) {
18
19
  this.types = types;
19
- this.includedTypes = {};
20
+ this.skipTypes = skipTypes;
20
21
  this.typeImports = new TypeImports_js_1.TypeImports();
21
- this.skipTypes = [];
22
+ this.knownTypes = this.calculateKnownTypes();
23
+ this.includedTypes = {};
22
24
  }
23
25
  shouldGenerateTypeIn(_id) {
24
26
  return false;
@@ -28,7 +30,17 @@ class BaseTypesGen {
28
30
  this.typeCache = {};
29
31
  this.typeImports.clear();
30
32
  }
31
- includeTypes() {
33
+ calculateKnownTypes() {
34
+ const typesWithPath = this.types.filter((one) => one.path.length > 0);
35
+ return typesWithPath.reduce((o, type) => {
36
+ const { path, id } = type;
37
+ const [knownType, codecName] = (0, known_codecs_js_1.checkKnownCodecType)(path.join('::'));
38
+ if (!knownType)
39
+ return o;
40
+ return { ...o, [id]: codecName };
41
+ }, {});
42
+ }
43
+ calculateIncludedTypes() {
32
44
  const pathsCount = new Map();
33
45
  const typesWithPath = this.types.filter((one) => one.path.length > 0);
34
46
  const skipIds = [];
@@ -60,14 +72,13 @@ class BaseTypesGen {
60
72
  return o;
61
73
  }
62
74
  const suffix = typeSuffixes.get(id) || '';
63
- let knownType = false;
64
75
  let name, nameOut;
65
- const [isKnownCodecType, codecName] = (0, known_codecs_js_1.checkKnownCodecType)(joinedPath);
66
- if (isKnownCodecType) {
67
- const codecType = (0, known_codecs_js_1.findKnownCodecType)(codecName);
76
+ const knownCodecName = this.knownTypes[id];
77
+ const knownType = !!knownCodecName;
78
+ if (knownType) {
79
+ const codecType = (0, known_codecs_js_1.findKnownCodecType)(knownCodecName);
68
80
  name = codecType.typeIn;
69
81
  nameOut = codecType.typeOut;
70
- knownType = true;
71
82
  }
72
83
  else if (PATH_RM_INDEX_1.includes(path[1])) {
73
84
  const newPath = path.slice();
@@ -77,7 +88,7 @@ class BaseTypesGen {
77
88
  else {
78
89
  name = this.cleanPath(path);
79
90
  }
80
- if (this.shouldGenerateTypeIn(id)) {
91
+ if (!knownType && this.shouldGenerateTypeIn(id)) {
81
92
  nameOut = name;
82
93
  name = name.endsWith('Like') ? name : `${name}Like`;
83
94
  }
@@ -14,14 +14,13 @@ class ConstructorQueryGen extends QueryGen_js_1.QueryGen {
14
14
  const template = (0, utils_js_1.compileTemplate)('typink/templates/constructor-query.hbs');
15
15
  return (0, utils_js_1.beautifySourceCode)(template({ importTypes, constructorsOut }));
16
16
  }
17
- generateMethodDef(def) {
17
+ generateMethodDef(def, optionsParamName = 'options') {
18
18
  const { args, returnType } = def;
19
- args.forEach(({ type: { type } }) => this.importType(type));
20
19
  const paramsOut = this.generateParamsOut(args);
21
20
  const typeOutRaw = this.typesGen.generateType(returnType.type, 0, true);
22
21
  // Unwrap langError result
23
22
  const typeOut = typeOutRaw.match(/^(\w+)<(.*), (.*)>$/).at(2);
24
- return `GenericConstructorQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ConstructorCallOptions) => Promise<GenericConstructorCallResult<${typeOut}, ContractInstantiateResult<ChainApi>>>>`;
23
+ return `GenericConstructorQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ConstructorCallOptions) => Promise<GenericConstructorCallResult<${typeOut}, ContractInstantiateResult<ChainApi>>>>`;
25
24
  }
26
25
  }
27
26
  exports.ConstructorQueryGen = ConstructorQueryGen;
@@ -14,11 +14,9 @@ class ConstructorTxGen extends QueryGen_js_1.QueryGen {
14
14
  const template = (0, utils_js_1.compileTemplate)('typink/templates/constructor-tx.hbs');
15
15
  return (0, utils_js_1.beautifySourceCode)(template({ importTypes, constructorsOut }));
16
16
  }
17
- generateMethodDef(def) {
18
- const { args } = def;
19
- args.forEach(({ type: { type } }) => this.importType(type));
20
- const paramsOut = this.generateParamsOut(args);
21
- return `GenericConstructorTxCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ConstructorTxOptions) => GenericInstantiateSubmittableExtrinsic<ChainApi>>`;
17
+ generateMethodDef(def, optionsParamName = 'options') {
18
+ const paramsOut = this.generateParamsOut(def.args);
19
+ return `GenericConstructorTxCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ConstructorTxOptions) => GenericInstantiateSubmittableExtrinsic<ChainApi>>`;
22
20
  }
23
21
  }
24
22
  exports.ConstructorTxGen = ConstructorTxGen;
@@ -23,7 +23,6 @@ class EventsGen extends QueryGen_js_1.QueryGen {
23
23
  }
24
24
  #generateEventDef(event) {
25
25
  const { args, label } = event;
26
- args.forEach(({ type: { type } }) => this.importType(type));
27
26
  const paramsOut = this.generateParamsOut(args);
28
27
  return `GenericContractEvent<'${(0, utils_1.stringPascalCase)(label)}', {${paramsOut}}>`;
29
28
  }
@@ -21,9 +21,17 @@ class IndexGen {
21
21
  typeImports.addContractType('GenericContractApi');
22
22
  typeImports.addChainType('SubstrateApi');
23
23
  typeImports.addPortableType(langErrorName);
24
+ const { contract: { name = '', version = '', authors = [] }, source: { language = '' }, } = this.contractMetadata;
25
+ const interfaceDocs = (0, utils_js_1.commentBlock)([
26
+ `@name: ${interfaceName}`, // prettier-end-here
27
+ `@contractName: ${name}`,
28
+ `@contractVersion: ${version}`,
29
+ `@authors: ${authors.join(', ')}`,
30
+ `@language: ${language}`,
31
+ ]);
24
32
  const importTypes = typeImports.toImports({ useSubPaths });
25
33
  const template = (0, utils_js_1.compileTemplate)('typink/templates/index.hbs');
26
- return (0, utils_js_1.beautifySourceCode)(template({ interfaceName, langErrorName, importTypes }));
34
+ return (0, utils_js_1.beautifySourceCode)(template({ interfaceName, interfaceDocs, langErrorName, importTypes }));
27
35
  }
28
36
  }
29
37
  exports.IndexGen = IndexGen;
@@ -25,50 +25,27 @@ class QueryGen {
25
25
  let callsOut = '';
26
26
  messages.forEach((messageDef) => {
27
27
  const { label, docs, selector, args } = messageDef;
28
- callsOut += `${(0, utils_js_1.commentBlock)(docs, '\n', args.map((arg) => `@param {${this.typesGen.generateType(arg.type.type, 1)}} ${(0, utils_1.stringCamelCase)(arg.label)}`), optionsTypeName ? `@param {${optionsTypeName}} options` : '', '\n', `@selector ${selector}`)}`;
29
- callsOut += `${(0, contracts_1.normalizeLabel)(label)}: ${this.generateMethodDef(messageDef)};\n\n`;
28
+ // In case there is an arg has label `options`,
29
+ // we use the name `_options` for the last options param
30
+ // This is just and edge case, so this approach works for now
31
+ const optionsParamName = args.some(({ label }) => label === 'options') ? '_options' : 'options';
32
+ callsOut += `${(0, utils_js_1.commentBlock)(docs, '\n', args.map((arg) => `@param {${this.typesGen.generateType(arg.type.type, 1)}} ${(0, utils_1.stringCamelCase)(arg.label)}`), optionsTypeName ? `@param {${optionsTypeName}} ${optionsParamName}` : '', '\n', `@selector ${selector}`)}`;
33
+ callsOut += `${(0, contracts_1.normalizeLabel)(label)}: ${this.generateMethodDef(messageDef, optionsParamName)};\n\n`;
30
34
  });
31
35
  return callsOut;
32
36
  }
33
- generateMethodDef(def) {
37
+ generateMethodDef(def, optionsParamName = 'options') {
34
38
  const { args, returnType } = def;
35
- this.importType(returnType.type);
36
- args.forEach(({ type: { type } }) => this.importType(type));
37
39
  const paramsOut = this.generateParamsOut(args);
38
40
  const typeOutRaw = this.typesGen.generateType(returnType.type, 0, true);
39
41
  // Unwrap langError result
40
42
  const typeOut = typeOutRaw.match(/^(\w+)<(.*), (.*)>$/).at(2);
41
- return `GenericContractQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ContractCallOptions) => Promise<GenericContractCallResult<${typeOut}, ContractCallResult<ChainApi>>>>`;
43
+ return `GenericContractQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ContractCallOptions) => Promise<GenericContractCallResult<${typeOut}, ContractCallResult<ChainApi>>>>`;
42
44
  }
43
45
  generateParamsOut(args) {
44
46
  return args
45
47
  .map(({ type: { type }, label }) => `${(0, utils_1.stringCamelCase)(label)}: ${this.typesGen.generateType(type, 1)}`)
46
48
  .join(', ');
47
49
  }
48
- importType(typeId) {
49
- const contractType = this.contractMetadata.types[typeId];
50
- const typeDef = (0, contracts_1.normalizeContractTypeDef)(this.contractMetadata.types[typeId].type.def);
51
- if (this.typesGen.includedTypes[contractType.id]) {
52
- this.typesGen.addTypeImport(this.typesGen.includedTypes[contractType.id].name);
53
- return;
54
- }
55
- const { type, value } = typeDef;
56
- switch (type) {
57
- case 'Compact':
58
- case 'Primitive':
59
- case 'BitSequence':
60
- return;
61
- case 'Struct':
62
- return value.fields.forEach(({ typeId }) => this.importType(typeId));
63
- case 'Enum':
64
- return value.members.forEach(({ fields }) => fields.map(({ typeId }) => this.importType(typeId)).flat());
65
- case 'Tuple':
66
- return value.fields.forEach((typeId) => this.importType(typeId));
67
- case 'SizedVec':
68
- return this.importType(value.typeParam);
69
- case 'Sequence':
70
- return this.importType(value.typeParam);
71
- }
72
- }
73
50
  }
74
51
  exports.QueryGen = QueryGen;
@@ -15,11 +15,9 @@ class TxGen extends QueryGen_js_1.QueryGen {
15
15
  const template = (0, utils_js_1.compileTemplate)('typink/templates/tx.hbs');
16
16
  return (0, utils_js_1.beautifySourceCode)(template({ importTypes, txCallsOut }));
17
17
  }
18
- generateMethodDef(def) {
19
- const { args } = def;
20
- args.forEach(({ type: { type } }) => this.importType(type));
21
- const paramsOut = this.generateParamsOut(args);
22
- return `GenericContractTxCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ContractTxOptions) => ContractSubmittableExtrinsic<ChainApi>>`;
18
+ generateMethodDef(def, optionsParamName = 'options') {
19
+ const paramsOut = this.generateParamsOut(def.args);
20
+ return `GenericContractTxCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ContractTxOptions) => ContractSubmittableExtrinsic<ChainApi>>`;
23
21
  }
24
22
  }
25
23
  exports.TxGen = TxGen;
@@ -8,21 +8,91 @@ const SKIP_TYPES = ['Result', 'Option'];
8
8
  class TypesGen extends index_js_1.BaseTypesGen {
9
9
  contractMetadata;
10
10
  constructor(contractMetadata) {
11
- super((0, contracts_1.extractContractTypes)(contractMetadata));
11
+ super((0, contracts_1.extractContractTypes)(contractMetadata), SKIP_TYPES);
12
12
  this.contractMetadata = contractMetadata;
13
- this.skipTypes = SKIP_TYPES;
14
- this.includedTypes = this.includeTypes();
13
+ this.includedTypes = this.calculateIncludedTypes();
15
14
  }
16
15
  generate(useSubPaths = false) {
17
16
  let defTypeOut = '';
18
17
  Object.values(this.includedTypes)
19
18
  .filter(({ skip, knownType }) => !(skip || knownType))
20
- .forEach(({ nameOut, id }) => {
19
+ .forEach(({ nameOut, name, id }) => {
21
20
  defTypeOut += `export type ${nameOut} = ${this.generateType(id, 0, true)};\n\n`;
21
+ if (this.shouldGenerateTypeIn(id)) {
22
+ defTypeOut += `export type ${name} = ${this.generateType(id, 0)};\n\n`;
23
+ }
22
24
  });
23
25
  const importTypes = this.typeImports.toImports({ excludeModules: ['./types'], useSubPaths });
24
26
  const template = (0, utils_js_1.compileTemplate)('typink/templates/types.hbs');
25
27
  return (0, utils_js_1.beautifySourceCode)(template({ importTypes, defTypeOut }));
26
28
  }
29
+ shouldGenerateTypeIn(id) {
30
+ const { messages, constructors } = this.contractMetadata.spec;
31
+ return ((this.#idInParameters(id, messages) || this.#idInParameters(id, constructors)) && this.#includeKnownTypes(id));
32
+ }
33
+ #idInParameters(id, messages) {
34
+ for (let message of messages) {
35
+ // prettier-ignore
36
+ const isIn = message.args.reduce((a, { type: { type: typeId } }) => a || this.#typeDependOn(typeId, id), false);
37
+ if (isIn)
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ // Find out does type depends on known types
43
+ // Example: `type TypeA = Struct { field: AccountId }` => includeKnownTypes(TypeA) === true
44
+ #includeKnownTypes(typeId) {
45
+ if (this.knownTypes[typeId])
46
+ return true;
47
+ const { types } = this.contractMetadata;
48
+ const defA = (0, contracts_1.normalizeContractTypeDef)(types[typeId].type.def);
49
+ const { type, value } = defA;
50
+ switch (type) {
51
+ case 'Struct':
52
+ return value.fields.some(({ typeId }) => this.#includeKnownTypes(typeId));
53
+ case 'Enum':
54
+ return value.members.some(({ fields }) => fields.some(({ typeId }) => this.#includeKnownTypes(typeId)));
55
+ case 'Tuple':
56
+ return value.fields.some((typeId) => this.#includeKnownTypes(typeId));
57
+ case 'Sequence':
58
+ case 'SizedVec':
59
+ const $innerType = this.types[value.typeParam].typeDef;
60
+ if ($innerType.type === 'Primitive' && $innerType.value.kind === 'u8') {
61
+ return true; // ByteLikes
62
+ }
63
+ else {
64
+ return this.#includeKnownTypes(value.typeParam);
65
+ }
66
+ case 'Primitive':
67
+ case 'Compact':
68
+ case 'BitSequence':
69
+ return false;
70
+ }
71
+ }
72
+ // Find out does typeA depends on typeB
73
+ // Example: `type TypeA = Struct { field: TypeB }` => typeDependOn(TypeA, TypeB) === true
74
+ #typeDependOn(typeA, typeB) {
75
+ if (typeA === typeB)
76
+ return true;
77
+ const { types } = this.contractMetadata;
78
+ const defA = (0, contracts_1.normalizeContractTypeDef)(types[typeA].type.def);
79
+ const { type, value } = defA;
80
+ // prettier-ignore
81
+ switch (type) {
82
+ case 'Struct':
83
+ return value.fields.reduce((a, { typeId }) => a || this.#typeDependOn(typeId, typeB), false);
84
+ case 'Enum':
85
+ return value.members.reduce((a, { fields }) => a || fields.reduce((a, { typeId }) => a || this.#typeDependOn(typeId, typeB), false), false);
86
+ case 'Tuple':
87
+ return value.fields.reduce((a, typeId) => a || this.#typeDependOn(typeId, typeB), false);
88
+ case 'Sequence':
89
+ case 'SizedVec':
90
+ return this.#typeDependOn(value.typeParam, typeB);
91
+ case 'Primitive':
92
+ case 'Compact':
93
+ case 'BitSequence':
94
+ return false;
95
+ }
96
+ }
27
97
  }
28
98
  exports.TypesGen = TypesGen;
@@ -38,6 +38,6 @@ async function generateContractTypes(metadata, contract, outDir = '.', extension
38
38
  fs_1.default.writeFileSync(constructorTxTypesFileName, await constructorTxGen.generate(useSubPaths));
39
39
  fs_1.default.writeFileSync(eventsTypesFile, await eventsGen.generate(useSubPaths));
40
40
  fs_1.default.writeFileSync(indexTypesFileName, await indexGen.generate(useSubPaths));
41
- return { interfaceName };
41
+ return { interfaceName, outputFolder: dirPath };
42
42
  }
43
43
  exports.generateContractTypes = generateContractTypes;
@@ -9,7 +9,7 @@ import { ContractEvents } from './events';
9
9
 
10
10
  export * from './types';
11
11
 
12
- export interface {{{interfaceName}}}<Rv extends RpcVersion = RpcV2, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends GenericContractApi<Rv, ChainApi> {
12
+ {{{interfaceDocs}}}export interface {{{interfaceName}}}<Rv extends RpcVersion = RpcVersion, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends GenericContractApi<Rv, ChainApi> {
13
13
  query: ContractQuery<ChainApi[Rv]>;
14
14
  tx: ContractTx<ChainApi[Rv]>;
15
15
  constructorQuery: ConstructorQuery<ChainApi[Rv]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dedot/codegen",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Generate types",
5
5
  "author": "Thang X. Vu <thang@coongcrafts.io>",
6
6
  "homepage": "https://github.com/dedotdev/dedot/tree/main/packages/codegen",
@@ -18,14 +18,14 @@
18
18
  "copy": "cp -R ./src/chaintypes/templates ./dist/chaintypes && cp -R ./src/chaintypes/templates ./dist/cjs/chaintypes && cp -R ./src/typink/templates ./dist/typink && cp -R ./src/typink/templates ./dist/cjs/typink"
19
19
  },
20
20
  "dependencies": {
21
- "@dedot/api": "0.4.1",
22
- "@dedot/codecs": "0.4.1",
23
- "@dedot/contracts": "0.4.1",
24
- "@dedot/providers": "0.4.1",
25
- "@dedot/runtime-specs": "0.4.1",
26
- "@dedot/shape": "0.4.1",
27
- "@dedot/types": "0.4.1",
28
- "@dedot/utils": "0.4.1",
21
+ "@dedot/api": "0.5.0",
22
+ "@dedot/codecs": "0.5.0",
23
+ "@dedot/contracts": "0.5.0",
24
+ "@dedot/providers": "0.5.0",
25
+ "@dedot/runtime-specs": "0.5.0",
26
+ "@dedot/shape": "0.5.0",
27
+ "@dedot/types": "0.5.0",
28
+ "@dedot/utils": "0.5.0",
29
29
  "handlebars": "^4.7.8",
30
30
  "prettier": "^3.3.3"
31
31
  },
@@ -34,7 +34,7 @@
34
34
  "directory": "dist"
35
35
  },
36
36
  "license": "Apache-2.0",
37
- "gitHead": "05b2a88dd2c78b17d68f0dd211e50be79e79852e",
37
+ "gitHead": "968148ade63e07644094fc033be719b320e18a18",
38
38
  "module": "./index.js",
39
39
  "types": "./index.d.ts",
40
40
  "exports": {
@@ -15,11 +15,13 @@ export declare abstract class BaseTypesGen {
15
15
  includedTypes: Record<TypeId, NamedType>;
16
16
  typeImports: TypeImports;
17
17
  skipTypes: string[];
18
- protected constructor(types: PortableType[]);
18
+ knownTypes: Record<TypeId, string>;
19
+ protected constructor(types: PortableType[], skipTypes?: string[]);
19
20
  shouldGenerateTypeIn(_id: TypeId): boolean;
20
21
  typeCache: Record<string, string>;
21
22
  clearCache(): void;
22
- includeTypes(): Record<TypeId, NamedType>;
23
+ protected calculateKnownTypes(): Record<TypeId, string>;
24
+ protected calculateIncludedTypes(): Record<TypeId, NamedType>;
23
25
  generateType(typeId: TypeId, nestedLevel?: number, typeOut?: boolean): string;
24
26
  generateObjectType(fields: Field[], nestedLevel?: number, typeOut?: boolean): string;
25
27
  cleanPath(path: string[]): string;
@@ -11,11 +11,13 @@ export class BaseTypesGen {
11
11
  includedTypes;
12
12
  typeImports;
13
13
  skipTypes;
14
- constructor(types) {
14
+ knownTypes;
15
+ constructor(types, skipTypes = []) {
15
16
  this.types = types;
16
- this.includedTypes = {};
17
+ this.skipTypes = skipTypes;
17
18
  this.typeImports = new TypeImports();
18
- this.skipTypes = [];
19
+ this.knownTypes = this.calculateKnownTypes();
20
+ this.includedTypes = {};
19
21
  }
20
22
  shouldGenerateTypeIn(_id) {
21
23
  return false;
@@ -25,7 +27,17 @@ export class BaseTypesGen {
25
27
  this.typeCache = {};
26
28
  this.typeImports.clear();
27
29
  }
28
- includeTypes() {
30
+ calculateKnownTypes() {
31
+ const typesWithPath = this.types.filter((one) => one.path.length > 0);
32
+ return typesWithPath.reduce((o, type) => {
33
+ const { path, id } = type;
34
+ const [knownType, codecName] = checkKnownCodecType(path.join('::'));
35
+ if (!knownType)
36
+ return o;
37
+ return { ...o, [id]: codecName };
38
+ }, {});
39
+ }
40
+ calculateIncludedTypes() {
29
41
  const pathsCount = new Map();
30
42
  const typesWithPath = this.types.filter((one) => one.path.length > 0);
31
43
  const skipIds = [];
@@ -57,14 +69,13 @@ export class BaseTypesGen {
57
69
  return o;
58
70
  }
59
71
  const suffix = typeSuffixes.get(id) || '';
60
- let knownType = false;
61
72
  let name, nameOut;
62
- const [isKnownCodecType, codecName] = checkKnownCodecType(joinedPath);
63
- if (isKnownCodecType) {
64
- const codecType = findKnownCodecType(codecName);
73
+ const knownCodecName = this.knownTypes[id];
74
+ const knownType = !!knownCodecName;
75
+ if (knownType) {
76
+ const codecType = findKnownCodecType(knownCodecName);
65
77
  name = codecType.typeIn;
66
78
  nameOut = codecType.typeOut;
67
- knownType = true;
68
79
  }
69
80
  else if (PATH_RM_INDEX_1.includes(path[1])) {
70
81
  const newPath = path.slice();
@@ -74,7 +85,7 @@ export class BaseTypesGen {
74
85
  else {
75
86
  name = this.cleanPath(path);
76
87
  }
77
- if (this.shouldGenerateTypeIn(id)) {
88
+ if (!knownType && this.shouldGenerateTypeIn(id)) {
78
89
  nameOut = name;
79
90
  name = name.endsWith('Like') ? name : `${name}Like`;
80
91
  }
package/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export interface GeneratedResult {
2
2
  interfaceName: string;
3
+ outputFolder: string;
3
4
  }
@@ -2,5 +2,5 @@ import { ContractConstructorMessage } from '@dedot/contracts';
2
2
  import { QueryGen } from './QueryGen.js';
3
3
  export declare class ConstructorQueryGen extends QueryGen {
4
4
  generate(useSubPaths?: boolean): Promise<string>;
5
- generateMethodDef(def: ContractConstructorMessage): string;
5
+ generateMethodDef(def: ContractConstructorMessage, optionsParamName?: string): string;
6
6
  }
@@ -11,13 +11,12 @@ export class ConstructorQueryGen extends QueryGen {
11
11
  const template = compileTemplate('typink/templates/constructor-query.hbs');
12
12
  return beautifySourceCode(template({ importTypes, constructorsOut }));
13
13
  }
14
- generateMethodDef(def) {
14
+ generateMethodDef(def, optionsParamName = 'options') {
15
15
  const { args, returnType } = def;
16
- args.forEach(({ type: { type } }) => this.importType(type));
17
16
  const paramsOut = this.generateParamsOut(args);
18
17
  const typeOutRaw = this.typesGen.generateType(returnType.type, 0, true);
19
18
  // Unwrap langError result
20
19
  const typeOut = typeOutRaw.match(/^(\w+)<(.*), (.*)>$/).at(2);
21
- return `GenericConstructorQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ConstructorCallOptions) => Promise<GenericConstructorCallResult<${typeOut}, ContractInstantiateResult<ChainApi>>>>`;
20
+ return `GenericConstructorQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ConstructorCallOptions) => Promise<GenericConstructorCallResult<${typeOut}, ContractInstantiateResult<ChainApi>>>>`;
22
21
  }
23
22
  }
@@ -2,5 +2,5 @@ import { ContractConstructorMessage } from '@dedot/contracts';
2
2
  import { QueryGen } from './QueryGen.js';
3
3
  export declare class ConstructorTxGen extends QueryGen {
4
4
  generate(useSubPaths?: boolean): Promise<string>;
5
- generateMethodDef(def: ContractConstructorMessage): string;
5
+ generateMethodDef(def: ContractConstructorMessage, optionsParamName?: string): string;
6
6
  }
@@ -11,10 +11,8 @@ export class ConstructorTxGen extends QueryGen {
11
11
  const template = compileTemplate('typink/templates/constructor-tx.hbs');
12
12
  return beautifySourceCode(template({ importTypes, constructorsOut }));
13
13
  }
14
- generateMethodDef(def) {
15
- const { args } = def;
16
- args.forEach(({ type: { type } }) => this.importType(type));
17
- const paramsOut = this.generateParamsOut(args);
18
- return `GenericConstructorTxCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ConstructorTxOptions) => GenericInstantiateSubmittableExtrinsic<ChainApi>>`;
14
+ generateMethodDef(def, optionsParamName = 'options') {
15
+ const paramsOut = this.generateParamsOut(def.args);
16
+ return `GenericConstructorTxCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ConstructorTxOptions) => GenericInstantiateSubmittableExtrinsic<ChainApi>>`;
19
17
  }
20
18
  }
@@ -20,7 +20,6 @@ export class EventsGen extends QueryGen {
20
20
  }
21
21
  #generateEventDef(event) {
22
22
  const { args, label } = event;
23
- args.forEach(({ type: { type } }) => this.importType(type));
24
23
  const paramsOut = this.generateParamsOut(args);
25
24
  return `GenericContractEvent<'${stringPascalCase(label)}', {${paramsOut}}>`;
26
25
  }
@@ -1,5 +1,5 @@
1
1
  import { TypeImports } from '../../shared/index.js';
2
- import { beautifySourceCode, compileTemplate } from '../../utils.js';
2
+ import { beautifySourceCode, commentBlock, compileTemplate } from '../../utils.js';
3
3
  export class IndexGen {
4
4
  interfaceName;
5
5
  contractMetadata;
@@ -18,8 +18,16 @@ export class IndexGen {
18
18
  typeImports.addContractType('GenericContractApi');
19
19
  typeImports.addChainType('SubstrateApi');
20
20
  typeImports.addPortableType(langErrorName);
21
+ const { contract: { name = '', version = '', authors = [] }, source: { language = '' }, } = this.contractMetadata;
22
+ const interfaceDocs = commentBlock([
23
+ `@name: ${interfaceName}`, // prettier-end-here
24
+ `@contractName: ${name}`,
25
+ `@contractVersion: ${version}`,
26
+ `@authors: ${authors.join(', ')}`,
27
+ `@language: ${language}`,
28
+ ]);
21
29
  const importTypes = typeImports.toImports({ useSubPaths });
22
30
  const template = compileTemplate('typink/templates/index.hbs');
23
- return beautifySourceCode(template({ interfaceName, langErrorName, importTypes }));
31
+ return beautifySourceCode(template({ interfaceName, interfaceDocs, langErrorName, importTypes }));
24
32
  }
25
33
  }
@@ -6,7 +6,6 @@ export declare class QueryGen {
6
6
  constructor(contractMetadata: ContractMetadata, typeGen: TypesGen);
7
7
  generate(useSubPaths?: boolean): Promise<string>;
8
8
  doGenerate(messages: ContractMessage[], optionsTypeName?: string): string;
9
- generateMethodDef(def: ContractMessage): string;
9
+ generateMethodDef(def: ContractMessage, optionsParamName?: string): string;
10
10
  generateParamsOut(args: ContractMessageArg[]): string;
11
- importType(typeId: number): any;
12
11
  }
@@ -1,4 +1,4 @@
1
- import { normalizeContractTypeDef, normalizeLabel, } from '@dedot/contracts';
1
+ import { normalizeLabel } from '@dedot/contracts';
2
2
  import { stringCamelCase } from '@dedot/utils';
3
3
  import { beautifySourceCode, commentBlock, compileTemplate } from '../../utils.js';
4
4
  export class QueryGen {
@@ -22,49 +22,26 @@ export class QueryGen {
22
22
  let callsOut = '';
23
23
  messages.forEach((messageDef) => {
24
24
  const { label, docs, selector, args } = messageDef;
25
- callsOut += `${commentBlock(docs, '\n', args.map((arg) => `@param {${this.typesGen.generateType(arg.type.type, 1)}} ${stringCamelCase(arg.label)}`), optionsTypeName ? `@param {${optionsTypeName}} options` : '', '\n', `@selector ${selector}`)}`;
26
- callsOut += `${normalizeLabel(label)}: ${this.generateMethodDef(messageDef)};\n\n`;
25
+ // In case there is an arg has label `options`,
26
+ // we use the name `_options` for the last options param
27
+ // This is just and edge case, so this approach works for now
28
+ const optionsParamName = args.some(({ label }) => label === 'options') ? '_options' : 'options';
29
+ callsOut += `${commentBlock(docs, '\n', args.map((arg) => `@param {${this.typesGen.generateType(arg.type.type, 1)}} ${stringCamelCase(arg.label)}`), optionsTypeName ? `@param {${optionsTypeName}} ${optionsParamName}` : '', '\n', `@selector ${selector}`)}`;
30
+ callsOut += `${normalizeLabel(label)}: ${this.generateMethodDef(messageDef, optionsParamName)};\n\n`;
27
31
  });
28
32
  return callsOut;
29
33
  }
30
- generateMethodDef(def) {
34
+ generateMethodDef(def, optionsParamName = 'options') {
31
35
  const { args, returnType } = def;
32
- this.importType(returnType.type);
33
- args.forEach(({ type: { type } }) => this.importType(type));
34
36
  const paramsOut = this.generateParamsOut(args);
35
37
  const typeOutRaw = this.typesGen.generateType(returnType.type, 0, true);
36
38
  // Unwrap langError result
37
39
  const typeOut = typeOutRaw.match(/^(\w+)<(.*), (.*)>$/).at(2);
38
- return `GenericContractQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ContractCallOptions) => Promise<GenericContractCallResult<${typeOut}, ContractCallResult<ChainApi>>>>`;
40
+ return `GenericContractQueryCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ContractCallOptions) => Promise<GenericContractCallResult<${typeOut}, ContractCallResult<ChainApi>>>>`;
39
41
  }
40
42
  generateParamsOut(args) {
41
43
  return args
42
44
  .map(({ type: { type }, label }) => `${stringCamelCase(label)}: ${this.typesGen.generateType(type, 1)}`)
43
45
  .join(', ');
44
46
  }
45
- importType(typeId) {
46
- const contractType = this.contractMetadata.types[typeId];
47
- const typeDef = normalizeContractTypeDef(this.contractMetadata.types[typeId].type.def);
48
- if (this.typesGen.includedTypes[contractType.id]) {
49
- this.typesGen.addTypeImport(this.typesGen.includedTypes[contractType.id].name);
50
- return;
51
- }
52
- const { type, value } = typeDef;
53
- switch (type) {
54
- case 'Compact':
55
- case 'Primitive':
56
- case 'BitSequence':
57
- return;
58
- case 'Struct':
59
- return value.fields.forEach(({ typeId }) => this.importType(typeId));
60
- case 'Enum':
61
- return value.members.forEach(({ fields }) => fields.map(({ typeId }) => this.importType(typeId)).flat());
62
- case 'Tuple':
63
- return value.fields.forEach((typeId) => this.importType(typeId));
64
- case 'SizedVec':
65
- return this.importType(value.typeParam);
66
- case 'Sequence':
67
- return this.importType(value.typeParam);
68
- }
69
- }
70
47
  }
@@ -2,5 +2,5 @@ import { ContractMessage } from '@dedot/contracts';
2
2
  import { QueryGen } from './QueryGen.js';
3
3
  export declare class TxGen extends QueryGen {
4
4
  generate(useSubPaths?: boolean): Promise<string>;
5
- generateMethodDef(def: ContractMessage): string;
5
+ generateMethodDef(def: ContractMessage, optionsParamName?: string): string;
6
6
  }
@@ -12,10 +12,8 @@ export class TxGen extends QueryGen {
12
12
  const template = compileTemplate('typink/templates/tx.hbs');
13
13
  return beautifySourceCode(template({ importTypes, txCallsOut }));
14
14
  }
15
- generateMethodDef(def) {
16
- const { args } = def;
17
- args.forEach(({ type: { type } }) => this.importType(type));
18
- const paramsOut = this.generateParamsOut(args);
19
- return `GenericContractTxCall<ChainApi, (${paramsOut && `${paramsOut},`} options: ContractTxOptions) => ContractSubmittableExtrinsic<ChainApi>>`;
15
+ generateMethodDef(def, optionsParamName = 'options') {
16
+ const paramsOut = this.generateParamsOut(def.args);
17
+ return `GenericContractTxCall<ChainApi, (${paramsOut && `${paramsOut},`} ${optionsParamName}: ContractTxOptions) => ContractSubmittableExtrinsic<ChainApi>>`;
20
18
  }
21
19
  }
@@ -1,7 +1,10 @@
1
+ import { TypeId } from '@dedot/codecs';
1
2
  import { ContractMetadata } from '@dedot/contracts';
2
3
  import { BaseTypesGen } from '../../shared/index.js';
3
4
  export declare class TypesGen extends BaseTypesGen {
5
+ #private;
4
6
  contractMetadata: ContractMetadata;
5
7
  constructor(contractMetadata: ContractMetadata);
6
8
  generate(useSubPaths?: boolean): Promise<string>;
9
+ shouldGenerateTypeIn(id: TypeId): boolean;
7
10
  }
@@ -1,24 +1,94 @@
1
- import { extractContractTypes } from '@dedot/contracts';
1
+ import { extractContractTypes, normalizeContractTypeDef } from '@dedot/contracts';
2
2
  import { BaseTypesGen } from '../../shared/index.js';
3
3
  import { beautifySourceCode, compileTemplate } from '../../utils.js';
4
4
  const SKIP_TYPES = ['Result', 'Option'];
5
5
  export class TypesGen extends BaseTypesGen {
6
6
  contractMetadata;
7
7
  constructor(contractMetadata) {
8
- super(extractContractTypes(contractMetadata));
8
+ super(extractContractTypes(contractMetadata), SKIP_TYPES);
9
9
  this.contractMetadata = contractMetadata;
10
- this.skipTypes = SKIP_TYPES;
11
- this.includedTypes = this.includeTypes();
10
+ this.includedTypes = this.calculateIncludedTypes();
12
11
  }
13
12
  generate(useSubPaths = false) {
14
13
  let defTypeOut = '';
15
14
  Object.values(this.includedTypes)
16
15
  .filter(({ skip, knownType }) => !(skip || knownType))
17
- .forEach(({ nameOut, id }) => {
16
+ .forEach(({ nameOut, name, id }) => {
18
17
  defTypeOut += `export type ${nameOut} = ${this.generateType(id, 0, true)};\n\n`;
18
+ if (this.shouldGenerateTypeIn(id)) {
19
+ defTypeOut += `export type ${name} = ${this.generateType(id, 0)};\n\n`;
20
+ }
19
21
  });
20
22
  const importTypes = this.typeImports.toImports({ excludeModules: ['./types'], useSubPaths });
21
23
  const template = compileTemplate('typink/templates/types.hbs');
22
24
  return beautifySourceCode(template({ importTypes, defTypeOut }));
23
25
  }
26
+ shouldGenerateTypeIn(id) {
27
+ const { messages, constructors } = this.contractMetadata.spec;
28
+ return ((this.#idInParameters(id, messages) || this.#idInParameters(id, constructors)) && this.#includeKnownTypes(id));
29
+ }
30
+ #idInParameters(id, messages) {
31
+ for (let message of messages) {
32
+ // prettier-ignore
33
+ const isIn = message.args.reduce((a, { type: { type: typeId } }) => a || this.#typeDependOn(typeId, id), false);
34
+ if (isIn)
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+ // Find out does type depends on known types
40
+ // Example: `type TypeA = Struct { field: AccountId }` => includeKnownTypes(TypeA) === true
41
+ #includeKnownTypes(typeId) {
42
+ if (this.knownTypes[typeId])
43
+ return true;
44
+ const { types } = this.contractMetadata;
45
+ const defA = normalizeContractTypeDef(types[typeId].type.def);
46
+ const { type, value } = defA;
47
+ switch (type) {
48
+ case 'Struct':
49
+ return value.fields.some(({ typeId }) => this.#includeKnownTypes(typeId));
50
+ case 'Enum':
51
+ return value.members.some(({ fields }) => fields.some(({ typeId }) => this.#includeKnownTypes(typeId)));
52
+ case 'Tuple':
53
+ return value.fields.some((typeId) => this.#includeKnownTypes(typeId));
54
+ case 'Sequence':
55
+ case 'SizedVec':
56
+ const $innerType = this.types[value.typeParam].typeDef;
57
+ if ($innerType.type === 'Primitive' && $innerType.value.kind === 'u8') {
58
+ return true; // ByteLikes
59
+ }
60
+ else {
61
+ return this.#includeKnownTypes(value.typeParam);
62
+ }
63
+ case 'Primitive':
64
+ case 'Compact':
65
+ case 'BitSequence':
66
+ return false;
67
+ }
68
+ }
69
+ // Find out does typeA depends on typeB
70
+ // Example: `type TypeA = Struct { field: TypeB }` => typeDependOn(TypeA, TypeB) === true
71
+ #typeDependOn(typeA, typeB) {
72
+ if (typeA === typeB)
73
+ return true;
74
+ const { types } = this.contractMetadata;
75
+ const defA = normalizeContractTypeDef(types[typeA].type.def);
76
+ const { type, value } = defA;
77
+ // prettier-ignore
78
+ switch (type) {
79
+ case 'Struct':
80
+ return value.fields.reduce((a, { typeId }) => a || this.#typeDependOn(typeId, typeB), false);
81
+ case 'Enum':
82
+ return value.members.reduce((a, { fields }) => a || fields.reduce((a, { typeId }) => a || this.#typeDependOn(typeId, typeB), false), false);
83
+ case 'Tuple':
84
+ return value.fields.reduce((a, typeId) => a || this.#typeDependOn(typeId, typeB), false);
85
+ case 'Sequence':
86
+ case 'SizedVec':
87
+ return this.#typeDependOn(value.typeParam, typeB);
88
+ case 'Primitive':
89
+ case 'Compact':
90
+ case 'BitSequence':
91
+ return false;
92
+ }
93
+ }
24
94
  }
package/typink/index.js CHANGED
@@ -32,5 +32,5 @@ export async function generateContractTypes(metadata, contract, outDir = '.', ex
32
32
  fs.writeFileSync(constructorTxTypesFileName, await constructorTxGen.generate(useSubPaths));
33
33
  fs.writeFileSync(eventsTypesFile, await eventsGen.generate(useSubPaths));
34
34
  fs.writeFileSync(indexTypesFileName, await indexGen.generate(useSubPaths));
35
- return { interfaceName };
35
+ return { interfaceName, outputFolder: dirPath };
36
36
  }
@@ -9,7 +9,7 @@ import { ContractEvents } from './events';
9
9
 
10
10
  export * from './types';
11
11
 
12
- export interface {{{interfaceName}}}<Rv extends RpcVersion = RpcV2, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends GenericContractApi<Rv, ChainApi> {
12
+ {{{interfaceDocs}}}export interface {{{interfaceName}}}<Rv extends RpcVersion = RpcVersion, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends GenericContractApi<Rv, ChainApi> {
13
13
  query: ContractQuery<ChainApi[Rv]>;
14
14
  tx: ContractTx<ChainApi[Rv]>;
15
15
  constructorQuery: ConstructorQuery<ChainApi[Rv]>;