use handlebars for code generation (#2228)
* use handlebars for code generation * update docs * fix isStrict * fix lint * Apply suggestions from code review Co-authored-by: Jaco Greeff <jacogr@gmail.com> * convert consts * keep empty lines in doc comments * update rpc * update tx * update interfaceRegistry * update tsDef * remove no longer used code Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
co-authored by
Jaco Greeff
parent
39ed76f2de
commit
174510a928
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ModuleMetadataLatest } from '@polkadot/types/interfaces/metadata';
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import staticData from '@polkadot/metadata/Metadata/static';
|
||||
import Metadata from '@polkadot/metadata/Metadata';
|
||||
@@ -10,29 +10,10 @@ import { TypeRegistry } from '@polkadot/types/create';
|
||||
import * as defaultDefs from '@polkadot/types/interfaces/definitions';
|
||||
import { stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import { FOOTER, HEADER, TypeImports, createDocComments, createImportCode, createImports, indent, registerDefinitions, setImports, writeFile } from '../util';
|
||||
import { createImports, compareName, readTemplate, registerDefinitions, setImports, writeFile } from '../util';
|
||||
|
||||
// Generate types for one module
|
||||
/** @internal */
|
||||
function generateModule (allDefs: object, { constants, name }: ModuleMetadataLatest, imports: TypeImports, isStrict: boolean): string[] {
|
||||
if (!constants.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isStrict) {
|
||||
setImports(allDefs, imports, ['Codec']);
|
||||
}
|
||||
|
||||
return [indent(4)(`${stringCamelCase(name.toString())}: {`)]
|
||||
.concat(isStrict ? '' : indent(6)('[index: string]: AugmentedConst<object & Codec>;'))
|
||||
.concat(constants.sort((a, b) => a.name.localeCompare(b.name.toString())).map(({ documentation, name, type }): string => {
|
||||
setImports(allDefs, imports, [type.toString()]);
|
||||
|
||||
return createDocComments(6, documentation) +
|
||||
indent(6)(`${stringCamelCase(name.toString())}: AugmentedConst<${type}>;`);
|
||||
}))
|
||||
.concat([indent(4)('};')]);
|
||||
}
|
||||
const template = readTemplate('consts');
|
||||
const generateForMetaTemplate = Handlebars.compile(template);
|
||||
|
||||
/** @internal */
|
||||
function generateForMeta (meta: Metadata, dest: string, extraTypes: Record<string, Record<string, { types: Record<string, any> }>>, isStrict: boolean): void {
|
||||
@@ -42,27 +23,47 @@ function generateForMeta (meta: Metadata, dest: string, extraTypes: Record<strin
|
||||
const allDefs = Object.entries(allTypes).reduce((defs, [path, obj]) => {
|
||||
return Object.entries(obj).reduce((defs, [key, value]) => ({ ...defs, [`${path}/${key}`]: value }), defs);
|
||||
}, {});
|
||||
const body = meta.asLatest.modules.sort((a, b) => a.name.localeCompare(b.name.toString())).reduce((acc, mod): string[] => {
|
||||
return acc.concat(generateModule(allDefs, mod, imports, isStrict));
|
||||
}, [] as string[]);
|
||||
const header = createImportCode(HEADER('chain'), imports, [
|
||||
|
||||
const modules = meta.asLatest.modules
|
||||
.sort(compareName)
|
||||
.filter((mod) => mod.constants.length > 0)
|
||||
.map(({ constants, name }) => {
|
||||
if (!isStrict) {
|
||||
setImports(allDefs, imports, ['Codec']);
|
||||
}
|
||||
|
||||
const items = constants
|
||||
.sort(compareName)
|
||||
.map(({ documentation, name, type }) => {
|
||||
setImports(allDefs, imports, [type.toString()]);
|
||||
|
||||
return {
|
||||
docs: documentation,
|
||||
name: stringCamelCase(name.toString()),
|
||||
type: type.toString()
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
items,
|
||||
name: stringCamelCase(name.toString())
|
||||
};
|
||||
});
|
||||
|
||||
const types = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
}))
|
||||
]);
|
||||
const interfaceStart = [
|
||||
"declare module '@polkadot/metadata/Decorated/consts/types' {",
|
||||
indent(2)('export interface Constants {\n')
|
||||
].join('\n');
|
||||
const interfaceEnd = `\n${indent(2)('}')}\n}`;
|
||||
];
|
||||
|
||||
return header
|
||||
.concat(interfaceStart)
|
||||
.concat(isStrict ? '' : indent(4)('[index: string]: ModuleConstants;\n'))
|
||||
.concat(body.join('\n'))
|
||||
.concat(interfaceEnd)
|
||||
.concat(FOOTER);
|
||||
return generateForMetaTemplate({
|
||||
headerType: 'chain',
|
||||
imports,
|
||||
isStrict,
|
||||
modules,
|
||||
types
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import Raw from '@polkadot/types/codec/Raw';
|
||||
import * as defaultDefinitions from '@polkadot/types/interfaces/definitions';
|
||||
import * as defaultPrimitives from '@polkadot/types/primitive';
|
||||
|
||||
import { FOOTER, HEADER, createImportCode, createImports, getDerivedTypes, indent, setImports, writeFile } from '../util';
|
||||
import { createImports, getDerivedTypes, readTemplate, setImports, writeFile } from '../util';
|
||||
|
||||
const primitiveClasses = {
|
||||
...defaultPrimitives,
|
||||
Raw
|
||||
};
|
||||
|
||||
const template = readTemplate('interfaceRegistry');
|
||||
const generateInterfaceTypesTemplate = Handlebars.compile(template);
|
||||
|
||||
/** @internal */
|
||||
export function generateInterfaceTypes (importDefinitions: { [importPath: string]: object }, dest: string): void {
|
||||
writeFile(dest, (): string => {
|
||||
@@ -21,52 +26,43 @@ export function generateInterfaceTypes (importDefinitions: { [importPath: string
|
||||
const imports = createImports(importDefinitions);
|
||||
const definitions = imports.definitions;
|
||||
|
||||
const primitives = Object
|
||||
const items: string[] = [];
|
||||
|
||||
Object
|
||||
.keys(primitiveClasses)
|
||||
.filter((name): boolean => !!name.indexOf('Generic'))
|
||||
.reduce((accumulator, primitiveName): string => {
|
||||
.forEach((primitiveName) => {
|
||||
setImports(definitions, imports, [primitiveName]);
|
||||
|
||||
return [
|
||||
accumulator,
|
||||
getDerivedTypes(definitions, primitiveName, primitiveName, imports).map(indent(4)).join('\n')
|
||||
].join('\n');
|
||||
}, '');
|
||||
items.push(...getDerivedTypes(definitions, primitiveName, primitiveName, imports));
|
||||
});
|
||||
|
||||
const existingTypes: Record<string, boolean> = {};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const srml = Object.entries(definitions).reduce((accumulator, [_defName, { types }]): string => {
|
||||
Object.entries(definitions).forEach(([, { types }]) => {
|
||||
setImports(definitions, imports, Object.keys(types));
|
||||
|
||||
const uniqueTypes = Object.keys(types).filter((type) => !existingTypes[type]);
|
||||
|
||||
uniqueTypes.forEach((type) => { existingTypes[type] = true; });
|
||||
uniqueTypes.forEach((type) => {
|
||||
existingTypes[type] = true;
|
||||
items.push(...getDerivedTypes(definitions, type, types[type], imports));
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
accumulator,
|
||||
...uniqueTypes.map((type): string =>
|
||||
getDerivedTypes(definitions, type, types[type], imports).map(indent(4)).join('\n')
|
||||
)
|
||||
].join('\n');
|
||||
}, '');
|
||||
|
||||
const header = createImportCode(HEADER('defs'), imports, [
|
||||
const types = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
}))
|
||||
]);
|
||||
];
|
||||
|
||||
const interfaceStart = "declare module '@polkadot/types/types/registry' {\n export interface InterfaceTypes {";
|
||||
const interfaceEnd = '\n }\n}';
|
||||
|
||||
return header
|
||||
.concat(interfaceStart)
|
||||
.concat(primitives)
|
||||
.concat(srml)
|
||||
.concat(interfaceEnd)
|
||||
.concat(FOOTER);
|
||||
return generateInterfaceTypesTemplate({
|
||||
headerType: 'defs',
|
||||
imports,
|
||||
items,
|
||||
types
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ModuleMetadataLatest, StorageEntryMetadataLatest } from '@polkadot/types/interfaces/metadata';
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { StorageEntryMetadataLatest } from '@polkadot/types/interfaces/metadata';
|
||||
import { Registry } from '@polkadot/types/types';
|
||||
|
||||
import staticData from '@polkadot/metadata/Metadata/static';
|
||||
@@ -12,7 +14,7 @@ import { unwrapStorageType } from '@polkadot/types/primitive/StorageKey';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import { stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import { FOOTER, HEADER, TypeImports, createDocComments, createImportCode, createImports, formatType, getSimilarTypes, indent, registerDefinitions, setImports, writeFile } from '../util';
|
||||
import { TypeImports, createImports, compareName, formatType, getSimilarTypes, readTemplate, registerDefinitions, setImports, writeFile } from '../util';
|
||||
|
||||
// From a storage entry metadata, we return [args, returnType]
|
||||
/** @internal */
|
||||
@@ -60,28 +62,8 @@ function entrySignature (allDefs: object, registry: Registry, storageEntry: Stor
|
||||
throw new Error(`entryArgs: Cannot parse args of entry ${storageEntry.name}`);
|
||||
}
|
||||
|
||||
// Generate types for one module
|
||||
/** @internal */
|
||||
function generateModule (allDefs: object, registry: Registry, { name, storage }: ModuleMetadataLatest, imports: TypeImports, isStrict: boolean): string[] {
|
||||
if (storage.isNone) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [indent(4)(`${stringCamelCase(name.toString())}: {`)]
|
||||
.concat(isStrict ? '' : indent(6)('[index: string]: QueryableStorageEntry<ApiType>;'))
|
||||
.concat(storage.unwrap().items.sort((a, b) => a.name.localeCompare(b.name.toString())).map((storageEntry): string => {
|
||||
const [args, returnType] = entrySignature(allDefs, registry, storageEntry, imports);
|
||||
let entryType = 'AugmentedQuery';
|
||||
|
||||
if (storageEntry.type.isDoubleMap) {
|
||||
entryType = `${entryType}DoubleMap`;
|
||||
}
|
||||
|
||||
return createDocComments(6, storageEntry.documentation) +
|
||||
indent(6)(`${stringCamelCase(storageEntry.name.toString())}: ${entryType}<ApiType, (${args}) => Observable<${returnType}>>${isStrict ? '' : ' & QueryableStorageEntry<ApiType>'};`);
|
||||
}))
|
||||
.concat([indent(4)('};')]);
|
||||
}
|
||||
const template = readTemplate('query');
|
||||
const generateForMetaTemplate = Handlebars.compile(template);
|
||||
|
||||
/** @internal */
|
||||
function generateForMeta (registry: Registry, meta: Metadata, dest: string, extraTypes: Record<string, Record<string, { types: Record<string, any> }>>, isStrict: boolean): void {
|
||||
@@ -91,13 +73,39 @@ function generateForMeta (registry: Registry, meta: Metadata, dest: string, extr
|
||||
const allDefs = Object.entries(allTypes).reduce((defs, [path, obj]) => {
|
||||
return Object.entries(obj).reduce((defs, [key, value]) => ({ ...defs, [`${path}/${key}`]: value }), defs);
|
||||
}, {});
|
||||
const body = meta.asLatest.modules.sort((a, b) => a.name.localeCompare(b.name.toString())).reduce((acc: string[], mod): string[] => {
|
||||
return acc.concat(generateModule(allDefs, registry, mod, imports, isStrict));
|
||||
}, []);
|
||||
|
||||
const modules = meta.asLatest.modules
|
||||
.sort(compareName)
|
||||
.filter((mod) => !mod.storage.isNone)
|
||||
.map(({ name, storage }) => {
|
||||
const items = storage.unwrap().items
|
||||
.sort(compareName)
|
||||
.map((storageEntry) => {
|
||||
const [args, returnType] = entrySignature(allDefs, registry, storageEntry, imports);
|
||||
let entryType = 'AugmentedQuery';
|
||||
|
||||
if (storageEntry.type.isDoubleMap) {
|
||||
entryType = `${entryType}DoubleMap`;
|
||||
}
|
||||
|
||||
return {
|
||||
args,
|
||||
docs: storageEntry.documentation,
|
||||
entryType,
|
||||
name: stringCamelCase(storageEntry.name.toString()),
|
||||
returnType
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
items,
|
||||
name: stringCamelCase(name.toString())
|
||||
};
|
||||
});
|
||||
|
||||
imports.typesTypes.Observable = true;
|
||||
|
||||
const header = createImportCode(HEADER('chain'), imports, [
|
||||
const types = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
@@ -106,24 +114,15 @@ function generateForMeta (registry: Registry, meta: Metadata, dest: string, extr
|
||||
file: '@polkadot/api/types',
|
||||
types: ['ApiTypes']
|
||||
}
|
||||
]);
|
||||
const interfaceStart = [
|
||||
"declare module '@polkadot/api/types/storage' {",
|
||||
indent(2)('export interface AugmentedQueries<ApiType> {\n')
|
||||
].join('\n');
|
||||
const interfaceEnd = `\n${indent(2)('}')}\n\n`;
|
||||
const queryableStorageInterface = indent(2)('export interface QueryableStorage<ApiType extends ApiTypes> extends AugmentedQueries<ApiType> {')
|
||||
.concat('\n')
|
||||
.concat(isStrict ? '' : indent(4)('[index: string]: QueryableModuleStorage<ApiType>;\n'))
|
||||
.concat(indent(2)('}\n'));
|
||||
];
|
||||
|
||||
return header
|
||||
.concat(interfaceStart)
|
||||
.concat(body.join('\n'))
|
||||
.concat(interfaceEnd)
|
||||
.concat(queryableStorageInterface)
|
||||
.concat('}')
|
||||
.concat(FOOTER);
|
||||
return generateForMetaTemplate({
|
||||
headerType: 'chain',
|
||||
imports,
|
||||
isStrict,
|
||||
modules,
|
||||
types
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import * as definitions from '@polkadot/types/interfaces/definitions';
|
||||
|
||||
import { FOOTER, HEADER, createDocComments, createImportCode, createImports, getSimilarTypes, setImports, writeFile, indent } from '../util';
|
||||
import { createImports, getSimilarTypes, readTemplate, setImports, writeFile } from '../util';
|
||||
|
||||
const StorageKeyTye = 'StorageKey | string | Uint8Array | any';
|
||||
|
||||
const template = readTemplate('rpc');
|
||||
const generateRpcTypesTemplate = Handlebars.compile(template);
|
||||
|
||||
/** @internal */
|
||||
export default function generateRpcTypes (dest = 'packages/api/src/augment/rpc.ts'): void {
|
||||
writeFile(dest, (): string => {
|
||||
@@ -22,62 +27,80 @@ export default function generateRpcTypes (dest = 'packages/api/src/augment/rpc.t
|
||||
.keys(definitions)
|
||||
.filter((key) => Object.keys(definitions[key as 'babe'].rpc || {}).length !== 0)
|
||||
.sort();
|
||||
const body = rpcKeys.reduce<string[]>((allSections, section): string[] => {
|
||||
const allMethods = Object.keys(definitions[section as 'babe'].rpc).sort().map((methodName): string => {
|
||||
const modules = rpcKeys.map((section) => {
|
||||
const allMethods = Object.keys(definitions[section as 'babe'].rpc).sort().map((methodName) => {
|
||||
const def = definitions[section as 'babe'].rpc[methodName];
|
||||
|
||||
let args;
|
||||
let type;
|
||||
let generic;
|
||||
|
||||
// These are too hard to type with generics, do manual overrides
|
||||
if (section === 'state') {
|
||||
setImports(allDefs, imports, ['Codec', 'Hash', 'StorageKey', 'Vec']);
|
||||
|
||||
if (methodName === 'getStorage') {
|
||||
return createDocComments(6, [def.description]) + indent(6)(`getStorage: AugmentedRpc<<T = Codec>(key: ${StorageKeyTye}, block?: Hash | Uint8Array | string) => Observable<T>>;`);
|
||||
generic = 'T = Codec';
|
||||
args = [`key: ${StorageKeyTye}, block?: Hash | Uint8Array | string`];
|
||||
type = 'T';
|
||||
} else if (methodName === 'queryStorage') {
|
||||
return createDocComments(6, [def.description]) + indent(6)(`queryStorage: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (${StorageKeyTye})[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>;`);
|
||||
generic = 'T = Codec[]';
|
||||
args = [`keys: Vec<StorageKey> | (${StorageKeyTye})[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string`];
|
||||
type = '[Hash, T][]';
|
||||
} else if (methodName === 'queryStorageAt') {
|
||||
return createDocComments(6, [def.description]) + indent(6)(`queryStorageAt: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (${StorageKeyTye})[], at?: Hash | Uint8Array | string) => Observable<T>>;`);
|
||||
generic = 'T = Codec[]';
|
||||
args = [`keys: Vec<StorageKey> | (${StorageKeyTye})[], at?: Hash | Uint8Array | string`];
|
||||
type = 'T';
|
||||
} else if (methodName === 'subscribeStorage') {
|
||||
return createDocComments(6, [def.description]) + indent(6)(`subscribeStorage: AugmentedRpc<<T = Codec[]>(keys?: Vec<StorageKey> | (${StorageKeyTye})[]) => Observable<T>>;`);
|
||||
generic = 'T = Codec[]';
|
||||
args = [`keys?: Vec<StorageKey> | (${StorageKeyTye})[]`];
|
||||
type = 'T';
|
||||
}
|
||||
}
|
||||
|
||||
setImports(allDefs, imports, [def.type]);
|
||||
if (args === undefined) {
|
||||
setImports(allDefs, imports, [def.type]);
|
||||
args = def.params.map((param: any) => {
|
||||
const similarTypes = getSimilarTypes(definitions, registry, param.type, imports);
|
||||
|
||||
const args = def.params.map((param: any): string => {
|
||||
const similarTypes = getSimilarTypes(definitions, registry, param.type, imports);
|
||||
setImports(allDefs, imports, [param.type, ...similarTypes]);
|
||||
|
||||
setImports(allDefs, imports, [param.type, ...similarTypes]);
|
||||
return `${param.name}${param.isOptional ? '?' : ''}: ${similarTypes.join(' | ')}`;
|
||||
});
|
||||
|
||||
return `${param.name}${param.isOptional ? '?' : ''}: ${similarTypes.join(' | ')}`;
|
||||
});
|
||||
type = def.type;
|
||||
generic = '';
|
||||
}
|
||||
|
||||
return createDocComments(6, [def.description]) + indent(6)(`${methodName}: AugmentedRpc<(${args.join(', ')}) => Observable<${def.type}>>;`);
|
||||
return {
|
||||
args: args.join(', '),
|
||||
docs: [def.description],
|
||||
generic,
|
||||
name: methodName,
|
||||
type
|
||||
};
|
||||
});
|
||||
|
||||
return allSections.concat(
|
||||
[
|
||||
` ${section}: {`,
|
||||
...allMethods,
|
||||
' };'
|
||||
].join('\n')
|
||||
);
|
||||
}, []).join('\n');
|
||||
return {
|
||||
items: allMethods,
|
||||
name: section
|
||||
};
|
||||
});
|
||||
|
||||
imports.typesTypes.Observable = true;
|
||||
|
||||
const header = createImportCode(HEADER('chain'), imports, [
|
||||
const types = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
}))
|
||||
]);
|
||||
const interfaceStart = "declare module '@polkadot/rpc-core/types.jsonrpc' {\n export interface RpcInterface {\n";
|
||||
const interfaceEnd = '\n }\n}';
|
||||
];
|
||||
|
||||
return header
|
||||
.concat(interfaceStart)
|
||||
.concat(body)
|
||||
.concat(interfaceEnd)
|
||||
.concat(FOOTER);
|
||||
return generateRpcTypesTemplate({
|
||||
headerType: 'chain',
|
||||
imports,
|
||||
modules,
|
||||
types
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { TypeDef, TypeDefInfo } from '@polkadot/types/create/types';
|
||||
|
||||
import path from 'path';
|
||||
@@ -9,7 +11,7 @@ import { getTypeDef } from '@polkadot/types/create';
|
||||
import * as defaultDefinitions from '@polkadot/types/interfaces/definitions';
|
||||
import { isString, stringCamelCase, stringUpperFirst } from '@polkadot/util';
|
||||
|
||||
import { FOOTER, HEADER, TypeImports, createImportCode, createImports, exportInterface, exportType, formatType, setImports, writeFile } from '../util';
|
||||
import { TypeImports, createImports, exportInterface, exportType, readTemplate, formatType, setImports, writeFile } from '../util';
|
||||
|
||||
interface Imports extends TypeImports {
|
||||
interfaces: [string, string][];
|
||||
@@ -181,22 +183,37 @@ function generateInterfaces (definitions: object, { types }: { types: Record<str
|
||||
});
|
||||
}
|
||||
|
||||
const templateIndex = readTemplate('tsDef/index');
|
||||
const generateTsDefIndexTemplate = Handlebars.compile(templateIndex);
|
||||
|
||||
const templateModuleTypes = readTemplate('tsDef/moduleTypes');
|
||||
const generateTsDefModuleTypesTemplate = Handlebars.compile(templateModuleTypes);
|
||||
|
||||
const templateTypes = readTemplate('tsDef/types');
|
||||
const generateTsDefTypesTemplate = Handlebars.compile(templateTypes);
|
||||
|
||||
/** @internal */
|
||||
function generateTsDefFor (importDefinitions: { [importPath: string]: object }, defName: string, { types }: { types: Record<string, any> }, outputDir: string): void {
|
||||
const imports = { ...createImports(importDefinitions, { types }), interfaces: [] } as Imports;
|
||||
const definitions = imports.definitions;
|
||||
const interfaces = generateInterfaces(definitions, { types }, imports);
|
||||
const sortedDefs = interfaces.sort((a, b): number => a[0].localeCompare(b[0])).map(([, definition]): string => definition).join('\n\n');
|
||||
const items = interfaces.sort((a, b): number => a[0].localeCompare(b[0])).map(([, definition]): string => definition);
|
||||
|
||||
const header = createImportCode(HEADER('defs'), imports, [
|
||||
const importTypes = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
}))
|
||||
]);
|
||||
];
|
||||
|
||||
writeFile(path.join(outputDir, defName, 'types.ts'), () => header.concat(sortedDefs).concat(`\n\nexport type PHANTOM_${defName.toUpperCase()} = '${defName}';`).concat(FOOTER), true);
|
||||
writeFile(path.join(outputDir, defName, 'index.ts'), () => HEADER('defs').concat('export * from \'./types\';').concat(FOOTER), true);
|
||||
writeFile(path.join(outputDir, defName, 'types.ts'), () => generateTsDefModuleTypesTemplate({
|
||||
headerType: 'defs',
|
||||
imports,
|
||||
items,
|
||||
name: defName,
|
||||
types: importTypes
|
||||
}), true);
|
||||
writeFile(path.join(outputDir, defName, 'index.ts'), () => generateTsDefIndexTemplate({ headerType: 'defs' }), true);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@@ -210,17 +227,13 @@ export function generateTsDef (importDefinitions: { [importPath: string]: object
|
||||
generateTsDefFor(importDefinitions, defName, obj, outputDir);
|
||||
});
|
||||
|
||||
return HEADER('defs')
|
||||
.concat(
|
||||
Object
|
||||
.keys(definitions)
|
||||
.map((moduleName): string => `export * from './${moduleName}/types';`)
|
||||
.join('\n')
|
||||
)
|
||||
.concat(FOOTER);
|
||||
return generateTsDefTypesTemplate({
|
||||
headerType: 'defs',
|
||||
items: Object.keys(definitions)
|
||||
});
|
||||
});
|
||||
|
||||
writeFile(path.join(outputDir, 'index.ts'), () => HEADER('defs').concat('export * from \'./types\';').concat(FOOTER), true);
|
||||
writeFile(path.join(outputDir, 'index.ts'), () => generateTsDefIndexTemplate({ headerType: 'defs' }), true);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ModuleMetadataLatest } from '@polkadot/types/interfaces/metadata';
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { Registry } from '@polkadot/types/types';
|
||||
|
||||
import staticData from '@polkadot/metadata/Metadata/static';
|
||||
@@ -12,7 +13,7 @@ import { Text } from '@polkadot/types/primitive';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import { stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import { FOOTER, HEADER, TypeImports, createDocComments, createImportCode, createImports, formatType, getSimilarTypes, indent, registerDefinitions, setImports, writeFile } from '../util';
|
||||
import { createImports, compareName, formatType, getSimilarTypes, readTemplate, registerDefinitions, setImports, writeFile } from '../util';
|
||||
|
||||
const MAPPED_NAMES: Record<string, string> = {
|
||||
new: 'updated'
|
||||
@@ -24,39 +25,8 @@ function mapName (_name: Text): string {
|
||||
return MAPPED_NAMES[name] || name;
|
||||
}
|
||||
|
||||
// Generate types for one module
|
||||
/** @internal */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function generateModule (registry: Registry, allDefs: object, { calls, name }: ModuleMetadataLatest, imports: TypeImports, isStrict: boolean): string[] {
|
||||
const allCalls = calls.unwrapOr(null);
|
||||
|
||||
if (!allCalls?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
setImports(allDefs, imports, ['Call', 'Extrinsic', 'SubmittableExtrinsic']);
|
||||
|
||||
return [indent(4)(`${stringCamelCase(name.toString())}: {`)]
|
||||
.concat(isStrict ? '' : indent(6)('[index: string]: SubmittableExtrinsicFunction<ApiType>;'))
|
||||
.concat(allCalls.sort((a, b) => a.name.localeCompare(b.name.toString())).map(({ args, documentation, name }): string => {
|
||||
const params = args
|
||||
.map(({ name, type }): [string, string, string] => {
|
||||
const typeStr = type.toString();
|
||||
const similarTypes = getSimilarTypes(allDefs, registry, typeStr, imports).map((type): string => formatType(allDefs, type, imports));
|
||||
const nameStr = mapName(name);
|
||||
|
||||
setImports(allDefs, imports, [...similarTypes.filter((type): boolean => !type.startsWith('(') && !type.startsWith('{')), typeStr]);
|
||||
|
||||
return [`${nameStr}: ${similarTypes.join(' | ')}`, nameStr, typeStr];
|
||||
})
|
||||
.map(([full]): string => full)
|
||||
.join(', ');
|
||||
|
||||
return createDocComments(6, documentation) +
|
||||
indent(6)(`${stringCamelCase(name.toString())}: AugmentedSubmittable<(${params}) => SubmittableExtrinsic<ApiType>>;`);
|
||||
}))
|
||||
.concat([indent(4)('};')]);
|
||||
}
|
||||
const template = readTemplate('tx');
|
||||
const generateForMetaTemplate = Handlebars.compile(template);
|
||||
|
||||
/** @internal */
|
||||
function generateForMeta (registry: Registry, meta: Metadata, dest: string, extraTypes: Record<string, Record<string, { types: Record<string, any> }>>, isStrict: boolean): void {
|
||||
@@ -66,10 +36,42 @@ function generateForMeta (registry: Registry, meta: Metadata, dest: string, extr
|
||||
const allDefs = Object.entries(allTypes).reduce((defs, [path, obj]) => {
|
||||
return Object.entries(obj).reduce((defs, [key, value]) => ({ ...defs, [`${path}/${key}`]: value }), defs);
|
||||
}, {});
|
||||
const body = meta.asLatest.modules.sort((a, b) => a.name.localeCompare(b.name.toString())).reduce((acc, mod): string[] => {
|
||||
return acc.concat(generateModule(registry, allDefs, mod, imports, isStrict));
|
||||
}, [] as string[]);
|
||||
const header = createImportCode(HEADER('chain'), imports, [
|
||||
|
||||
const modules = meta.asLatest.modules
|
||||
.sort(compareName)
|
||||
.filter(({ calls }) => calls.unwrapOr([]).length)
|
||||
.map(({ calls, name }) => {
|
||||
setImports(allDefs, imports, ['Call', 'Extrinsic', 'SubmittableExtrinsic']);
|
||||
|
||||
const items = calls.unwrap()
|
||||
.sort(compareName)
|
||||
.map(({ args, documentation, name }) => {
|
||||
const params = args
|
||||
.map(({ name, type }) => {
|
||||
const typeStr = type.toString();
|
||||
const similarTypes = getSimilarTypes(allDefs, registry, typeStr, imports).map((type): string => formatType(allDefs, type, imports));
|
||||
const nameStr = mapName(name);
|
||||
|
||||
setImports(allDefs, imports, [...similarTypes.filter((type): boolean => !type.startsWith('(') && !type.startsWith('{')), typeStr]);
|
||||
|
||||
return `${nameStr}: ${similarTypes.join(' | ')}`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
return {
|
||||
docs: documentation,
|
||||
name: stringCamelCase(name.toString()),
|
||||
params
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
items,
|
||||
name: stringCamelCase(name.toString())
|
||||
};
|
||||
});
|
||||
|
||||
const types = [
|
||||
...Object.keys(imports.localTypes).sort().map((packagePath): { file: string; types: string[] } => ({
|
||||
file: packagePath,
|
||||
types: Object.keys(imports.localTypes[packagePath])
|
||||
@@ -78,25 +80,15 @@ function generateForMeta (registry: Registry, meta: Metadata, dest: string, extr
|
||||
file: '@polkadot/api/types',
|
||||
types: ['ApiTypes', 'SubmittableExtrinsic']
|
||||
}
|
||||
]);
|
||||
const interfaceStart = [
|
||||
"declare module '@polkadot/api/types/submittable' {",
|
||||
indent(2)('export interface AugmentedSubmittables<ApiType> {\n')
|
||||
].join('\n');
|
||||
const interfaceEnd = `\n${indent(2)('}')}\n\n`;
|
||||
const submittableExtrinsicsInterface = indent(2)('export interface SubmittableExtrinsics<ApiType extends ApiTypes> extends AugmentedSubmittables<ApiType> {')
|
||||
.concat('\n')
|
||||
.concat(indent(4)('(extrinsic: Call | Extrinsic | Uint8Array | string): SubmittableExtrinsic<ApiType>;\n'))
|
||||
.concat(isStrict ? '' : indent(4)('[index: string]: SubmittableModuleExtrinsics<ApiType>;\n'))
|
||||
.concat(indent(2)('}\n'));
|
||||
];
|
||||
|
||||
return header
|
||||
.concat(interfaceStart)
|
||||
.concat(body.join('\n'))
|
||||
.concat(interfaceEnd)
|
||||
.concat(submittableExtrinsicsInterface)
|
||||
.concat('}')
|
||||
.concat(FOOTER);
|
||||
return generateForMetaTemplate({
|
||||
headerType: 'chain',
|
||||
imports,
|
||||
isStrict,
|
||||
modules,
|
||||
types
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
declare module '@polkadot/metadata/Decorated/consts/types' {
|
||||
export interface Constants {
|
||||
{{#unless isStrict}}
|
||||
[index: string]: ModuleConstants;
|
||||
{{/unless}}
|
||||
{{#each modules}}
|
||||
{{{name}}}: {
|
||||
{{#unless @root.isStrict}}
|
||||
[index: string]: AugmentedConst<object & Codec>;
|
||||
{{/unless}}
|
||||
{{~#each items}}
|
||||
{{> docs}}
|
||||
{{{name}}}: AugmentedConst<{{{type}}}>;
|
||||
{{/each}}
|
||||
};
|
||||
{{/each}}
|
||||
}
|
||||
}
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,7 @@
|
||||
{{#if docs.length}}
|
||||
/**
|
||||
{{#each docs}}
|
||||
* {{#trim}}{{{this}}}{{/trim}}
|
||||
{{/each}}
|
||||
**/
|
||||
{{/if}}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Auto-generated via `yarn polkadot-types-from-{{headerType}}`, do not edit
|
||||
/* eslint-disable */
|
||||
@@ -0,0 +1,12 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
declare module '@polkadot/types/types/registry' {
|
||||
export interface InterfaceTypes {
|
||||
{{#each items}}
|
||||
{{{this}}}
|
||||
{{/each}}
|
||||
}
|
||||
}
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
declare module '@polkadot/api/types/storage' {
|
||||
export interface AugmentedQueries<ApiType> {
|
||||
{{#each modules}}
|
||||
{{{name}}}: {
|
||||
{{#unless @root.isStrict}}
|
||||
[index: string]: QueryableStorageEntry<ApiType>;
|
||||
{{/unless}}
|
||||
{{~#each items}}
|
||||
{{> docs}}
|
||||
{{{name}}}: {{{entryType}}}<ApiType, ({{{args}}}) => Observable<{{{returnType}}}>>{{#unless @root.isStrict}} & QueryableStorageEntry<ApiType>{{/unless}};
|
||||
{{/each}}
|
||||
};
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
export interface QueryableStorage<ApiType extends ApiTypes> extends AugmentedQueries<ApiType> {
|
||||
{{#unless isStrict}}
|
||||
[index: string]: QueryableModuleStorage<ApiType>;
|
||||
{{/unless}}
|
||||
}
|
||||
}
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
declare module '@polkadot/rpc-core/types.jsonrpc' {
|
||||
export interface RpcInterface {
|
||||
{{#each modules}}
|
||||
{{{name}}}: {
|
||||
{{#each items}}
|
||||
{{> docs}}
|
||||
{{{name}}}: AugmentedRpc<{{#if generic}}<{{{generic}}}>{{/if}}({{{args}}}) => Observable<{{{type}}}>>;
|
||||
{{/each}}
|
||||
};
|
||||
{{/each}}
|
||||
}
|
||||
}
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,4 @@
|
||||
{{> header }}
|
||||
|
||||
export * from './types';
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,11 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
{{#each items}}
|
||||
{{{this}}}
|
||||
|
||||
{{/each}}
|
||||
|
||||
export type PHANTOM_{{#upper}}{{{name}}}{{/upper}} = '{{{name}}}';
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,6 @@
|
||||
{{> header }}
|
||||
|
||||
{{#each items}}
|
||||
export * from './{{{this}}}/types';
|
||||
{{/each}}
|
||||
{{> footer }}
|
||||
@@ -0,0 +1,27 @@
|
||||
{{> header }}
|
||||
|
||||
{{{ imports }}}
|
||||
|
||||
declare module '@polkadot/api/types/submittable' {
|
||||
export interface AugmentedSubmittables<ApiType> {
|
||||
{{#each modules}}
|
||||
{{{name}}}: {
|
||||
{{#unless @root.isStrict}}
|
||||
[index: string]: SubmittableExtrinsicFunction<ApiType>;
|
||||
{{/unless}}
|
||||
{{~#each items}}
|
||||
{{> docs}}
|
||||
{{{name}}}: AugmentedSubmittable<({{{params}}}) => SubmittableExtrinsic<ApiType>>;
|
||||
{{/each}}
|
||||
};
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsics<ApiType extends ApiTypes> extends AugmentedSubmittables<ApiType> {
|
||||
(extrinsic: Call | Extrinsic | Uint8Array | string): SubmittableExtrinsic<ApiType>;
|
||||
{{#unless isStrict}}
|
||||
[index: string]: SubmittableModuleExtrinsics<ApiType>;
|
||||
{{/unless}}
|
||||
}
|
||||
}
|
||||
{{> footer }}
|
||||
@@ -2,22 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Text } from '@polkadot/types';
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { indent } from './formatting';
|
||||
import { readTemplate } from './file';
|
||||
|
||||
type AnyString = Text | string;
|
||||
type Arg = [AnyString, AnyString];
|
||||
|
||||
export function createDocComments (spaces: number, docs: AnyString[], args: Arg[] = []): string {
|
||||
const contents = [
|
||||
...docs.map((doc): string => doc.toString().trim()).filter((doc): boolean => !!doc),
|
||||
...args.map(([name, type]): string => `@param ${name} ${type}`)
|
||||
].map((d): string => ` * ${d}`);
|
||||
|
||||
return contents.length
|
||||
? ['/**', ...contents, ' **/\n']
|
||||
.map((s): string => indent(spaces)(s))
|
||||
.join('\n')
|
||||
: '';
|
||||
}
|
||||
Handlebars.registerPartial({
|
||||
docs: Handlebars.compile(readTemplate('docs'))
|
||||
});
|
||||
|
||||
@@ -19,3 +19,7 @@ export function writeFile (dest: string, generator: () => string, noLog?: boolea
|
||||
|
||||
!noLog && console.log('');
|
||||
}
|
||||
|
||||
export function readTemplate (path: string): string {
|
||||
return fs.readFileSync(`${__dirname}/../templates/${path}.hbs`).toString();
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2017-2020 @polkadot/typegen authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { indent } from './formatting';
|
||||
|
||||
describe('formatting', () => {
|
||||
it('should indent correctly', () => {
|
||||
expect(indent(2)('foo')).toBe(' foo');
|
||||
});
|
||||
});
|
||||
@@ -2,59 +2,74 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
import { TypeDef, TypeDefInfo } from '@polkadot/types/create/types';
|
||||
|
||||
import { getTypeDef } from '@polkadot/types/create';
|
||||
import { paramsNotation } from '@polkadot/types/codec/utils';
|
||||
|
||||
import { setImports, TypeImports } from './imports';
|
||||
|
||||
export const HEADER = (type: 'chain' | 'defs'): string => `// Auto-generated via \`yarn polkadot-types-from-${type}\`, do not edit\n/* eslint-disable */\n\n`;
|
||||
export const FOOTER = '\n';
|
||||
import { readTemplate } from './file';
|
||||
|
||||
const TYPES_NON_PRIMITIVE = ['Metadata'];
|
||||
|
||||
// creates the import lines
|
||||
/** @internal */
|
||||
export function createImportCode (header: string, imports: TypeImports, checks: { file: string; types: string[] }[]): string {
|
||||
return [
|
||||
{
|
||||
file: '@polkadot/types/types',
|
||||
types: Object.keys(imports.typesTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/codec',
|
||||
types: Object
|
||||
.keys(imports.codecTypes)
|
||||
.filter((name): boolean => name !== 'Tuple')
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/extrinsic',
|
||||
types: Object.keys(imports.extrinsicTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/generic',
|
||||
types: Object.keys(imports.genericTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/primitive',
|
||||
types: Object
|
||||
.keys(imports.primitiveTypes)
|
||||
.filter((name): boolean => !TYPES_NON_PRIMITIVE.includes(name))
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types',
|
||||
types: Object
|
||||
.keys(imports.primitiveTypes)
|
||||
.filter((name): boolean => TYPES_NON_PRIMITIVE.includes(name))
|
||||
},
|
||||
...checks
|
||||
].reduce((result, { file, types }): string => {
|
||||
return types.length
|
||||
? `${result}import { ${types.sort().join(', ')} } from '${file}';\n`
|
||||
: result;
|
||||
}, header) + '\n';
|
||||
}
|
||||
export const HEADER = (type: 'chain' | 'defs'): string => `// Auto-generated via \`yarn polkadot-types-from-${type}\`, do not edit\n/* eslint-disable */\n\n`;
|
||||
|
||||
Handlebars.registerPartial({
|
||||
footer: Handlebars.compile(readTemplate('footer')),
|
||||
header: Handlebars.compile(readTemplate('header'))
|
||||
});
|
||||
|
||||
Handlebars.registerHelper({
|
||||
imports () {
|
||||
const { imports, types } = this as any;
|
||||
|
||||
return [
|
||||
{
|
||||
file: '@polkadot/types/types',
|
||||
types: Object.keys(imports.typesTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/codec',
|
||||
types: Object
|
||||
.keys(imports.codecTypes)
|
||||
.filter((name): boolean => name !== 'Tuple')
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/extrinsic',
|
||||
types: Object.keys(imports.extrinsicTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/generic',
|
||||
types: Object.keys(imports.genericTypes)
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types/primitive',
|
||||
types: Object
|
||||
.keys(imports.primitiveTypes)
|
||||
.filter((name): boolean => !TYPES_NON_PRIMITIVE.includes(name))
|
||||
},
|
||||
{
|
||||
file: '@polkadot/types',
|
||||
types: Object
|
||||
.keys(imports.primitiveTypes)
|
||||
.filter((name): boolean => TYPES_NON_PRIMITIVE.includes(name))
|
||||
},
|
||||
...types
|
||||
].reduce((result, { file, types }): string => {
|
||||
return types.length
|
||||
? `${result}import { ${types.sort().join(', ')} } from '${file}';\n`
|
||||
: result;
|
||||
}, '');
|
||||
},
|
||||
trim (options) {
|
||||
return options.fn(this).trim();
|
||||
},
|
||||
upper (options) {
|
||||
return options.fn(this).toUpperCase();
|
||||
}
|
||||
});
|
||||
|
||||
// helper to generate a `export interface <Name> extends <Base> {<Body>}
|
||||
/** @internal */
|
||||
@@ -273,13 +288,3 @@ export function formatType (definitions: object, type: string | TypeDef, imports
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indent a string with `n` spaces before.
|
||||
*/
|
||||
/** @internal */
|
||||
export function indent (n: number, char = ' '): (str: string) => string {
|
||||
return function (str: string): string {
|
||||
return `${char.repeat(n)}${str}`;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,3 +8,6 @@ export * from './file';
|
||||
export * from './formatting';
|
||||
export * from './imports';
|
||||
export * from './register';
|
||||
|
||||
export const compareName = (a: { name: { toString(): string } }, b: { name: { toString(): string } }): number =>
|
||||
a.name.toString().localeCompare(b.name.toString());
|
||||
|
||||
Reference in New Issue
Block a user