UNPKG

@cosmwasm/ts-codegen

Version:

@cosmwasm/ts-codegen converts your CosmWasm smart contracts into dev-friendly TypeScript classes so you can focus on shipping code.

129 lines (128 loc) 4.88 kB
import generate from '@babel/generator'; import * as t from '@babel/types'; import { BuilderContext, defaultOptions, } from '@cosmwasm/ts-codegen-ast'; import { pascal } from 'case'; import deepmerge from 'deepmerge'; import { writeFileSync } from 'fs'; import { sync as mkdirp } from 'mkdirp'; import { join } from 'path'; import { basename } from 'path'; import { createFileBundle, recursiveModuleBundle } from '../bundler'; import { createHelpers } from '../helpers/create-helpers'; import { ClientPlugin } from '../plugins/client'; import { MessageBuilderPlugin } from '../plugins/message-builder'; import { MessageComposerPlugin } from '../plugins/message-composer'; import { ContractsContextProviderPlugin } from '../plugins/provider'; import { ContractsProviderBundlePlugin } from '../plugins/provider-bundle'; import { ReactQueryPlugin } from '../plugins/react-query'; import { RecoilPlugin } from '../plugins/recoil'; import { TypesPlugin } from '../plugins/types'; import { readSchemas } from '../utils'; import { createDefaultContractInfo } from '../utils/contracts'; import { header } from '../utils/header'; const defaultOpts = { bundle: { enabled: true, scope: 'contracts', bundleFile: 'bundle.ts', }, useShorthandCtor: true, }; function getContract(contractOpt) { if (typeof contractOpt === 'string') { const name = basename(contractOpt); const contractName = pascal(name); return { name: contractName, dir: contractOpt, }; } return { name: pascal(contractOpt.name), dir: contractOpt.dir, }; } export class TSBuilder { contracts; outPath; options; plugins = []; builderContext = new BuilderContext(); files = []; loadDefaultPlugins() { this.plugins.push(new TypesPlugin(this.options), new ClientPlugin(this.options), new MessageComposerPlugin(this.options), new ReactQueryPlugin(this.options), new RecoilPlugin(this.options), new MessageBuilderPlugin(this.options), new ContractsContextProviderPlugin(this.options), new ContractsProviderBundlePlugin(this.options)); } constructor({ contracts, outPath, options, plugins }) { this.contracts = contracts; this.outPath = outPath; this.options = deepmerge(deepmerge(defaultOptions, defaultOpts), options ?? {}); this.loadDefaultPlugins(); if (plugins && plugins.length) { this.plugins.push(...plugins); } this.plugins.forEach((plugin) => plugin.setBuilder(this)); } async build() { await this.process(); await this.after(); } // lifecycle functions async process() { for (const contractOpt of this.contracts) { const contract = getContract(contractOpt); //resolve contract schema. const contractInfo = await readSchemas({ schemaDir: contract.dir, }); //lifecycle and plugins. await this.render('main', contract.name, contractInfo); } } async render(lifecycle, name, contractInfo) { const plugins = lifecycle ? this.plugins.filter((p) => p.lifecycle === lifecycle) : this.plugins; for (const plugin of plugins) { let files = await plugin.render(this.outPath, name, contractInfo ?? createDefaultContractInfo()); if (files && files.length) { this.files.push(...files); } } } async after() { await this.render('after'); const helpers = createHelpers({ outPath: this.outPath, contracts: this.contracts, options: this.options, plugins: this.plugins, }, this.builderContext); if (helpers && helpers.length) { this.files.push(...helpers); } if (this.options.bundle.enabled) { this.bundle(); } } async bundle() { const allFiles = this.files; const bundleFile = this.options.bundle.bundleFile; const bundlePath = join(this.options?.bundle?.bundlePath ?? this.outPath, bundleFile); const bundleVariables = {}; const importPaths = []; allFiles.forEach((file) => { createFileBundle(`${this.options.bundle.scope}.${file.contract}`, file.filename, bundlePath, importPaths, bundleVariables); }); const ast = recursiveModuleBundle(bundleVariables); const nodes = [...importPaths, ...ast]; // @ts-ignore let code = generate(t.program(nodes)).code; if (this.options?.bundle?.bundlePath) { mkdirp(this.options?.bundle?.bundlePath); } mkdirp(this.outPath); if (code.trim() === '') code = 'export {};'; writeFileSync(bundlePath, header + code); } }