UNPKG

@kadena/kadena-cli

Version:

Kadena CLI tool to interact with the Kadena blockchain (manage keys, transactions, etc.)

153 lines 7.27 kB
import { Pact } from '@kadena/client'; import { generateDts, pactParser } from '@kadena/pactjs-generator'; import { EOL } from 'os'; import { dirname, join } from 'path'; import * as prettier from 'prettier'; import { log } from '../../utils/logger.js'; import { getVersion } from '../../utils/version.js'; import { fetchModule } from './utils/callLocal.js'; import { getAPIUrl, getChainId } from './utils/chainHelpers.js'; import { extractImportedFiles } from './utils/extractImportedFiles.js'; import { shallowFindFile } from './utils/shallowFindFile.js'; import { verifyTsconfigTypings } from './utils/verifyTsconfigTypings.js'; export const TARGET_PACKAGE = '.kadena/pactjs-generated'; export class PactJSService { // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/parameter-properties constructor(services) { this.services = services; } async retrieveContractFromChain(module, chainId, network, networkConfig, api) { const apiHost = getAPIUrl({ api, networkConfig, chainId }); if (apiHost === undefined) { log.error(`Could not retrieve ${module} from network:${network} - chain:${chainId}`); throw new Error(`Could not retrieve ${module} from network:${network} - chain:${chainId}`); } const command = Pact.builder .execution(`(describe-module "${module}")`) .setNetworkId(networkConfig.networkId) .setMeta({ chainId: chainId.toString() }) .createTransaction(); const { code, error } = await fetchModule(apiHost, JSON.stringify(command)); if (error !== undefined) { log.error(`Could not retrieve ${module} from network:${network} - chain:${chainId}\nerror: ${error}`); throw new Error(`Could not retrieve ${module} from network:${network} - chain:${chainId}\nerror: ${error}`); } return code; } async generate(args) { const targetPackageJson = await shallowFindFile(process.cwd(), 'package.json'); if (targetPackageJson === null || targetPackageJson === undefined) { throw new Error('Could not find package.json'); } const moduleDtss = await this._generator(args); const targetDirectory = join(dirname(targetPackageJson), 'node_modules', '.kadena/pactjs-generated'); if (args.clean === true) { log.info(`Cleaning ${targetDirectory}`); try { await this.services.filesystem.deleteDirectory(targetDirectory); log.info(`Successfully cleaned ${targetDirectory}`); } catch (error) { log.error(`Failed to clean ${targetDirectory}:`, error); } } const fs = this.services.filesystem; if (!(await fs.directoryExists(targetDirectory))) { log.info(`Creating directory ${targetDirectory}`); await fs.ensureDirectoryExists(targetDirectory); } const indexPath = join(targetDirectory, 'index.d.ts'); await Promise.all([...moduleDtss].map(async ([moduleName, dtsContent]) => { const targetFilePath = join(targetDirectory, `${moduleName}.d.ts`); const formatted = await prettier.format(dtsContent, { parser: 'typescript', }); await fs.writeFile(targetFilePath, formatted); })); let indexDts = (await fs.fileExists(indexPath)) ? await fs.readFile(indexPath) : ''; if (indexDts === null || indexDts === undefined) { indexDts = ''; } const importedFiles = extractImportedFiles(indexDts); for (const moduleName of moduleDtss.keys()) { if (!importedFiles.includes(moduleName)) { importedFiles.push(moduleName); } } const version = await getVersion(); const doNotEdit = `/**${EOL} * THIS FILE IS GENERATED BY pactjs-cli (${version}). DO NOT EDIT IT${EOL} */`; const updatedIndexDts = `${doNotEdit}\n${importedFiles .sort() .map((moduleName) => `import './${moduleName}';`) .join('')}`; await fs.writeFile(indexPath, await prettier.format(updatedIndexDts, { parser: 'typescript' })); const defaultPackageJsonPath = join(targetDirectory, 'package.json'); if (!(await fs.fileExists(defaultPackageJsonPath))) { await fs.writeFile(defaultPackageJsonPath, JSON.stringify({ name: TARGET_PACKAGE, version, description: 'TypeScript definitions for @kadena/client', types: 'index.d.ts', keywords: ['pact', 'contract', 'pactjs'], author: `@kadena/pactjs-cli@${version}`, }, null, 2)); } else { log.error(`Package.json already exists at ${defaultPackageJsonPath}`); } const tsconfigPath = await shallowFindFile(process.cwd(), 'tsconfig.json'); await verifyTsconfigTypings(tsconfigPath); } async _generator(args) { if (args.contract) { log.info(`Generating pact contracts from chainweb for ${args.contract.join(',')}`); } if (args.file) { log.info(`Generating pact contracts from files for ${args.file.join(',')}`); } const getContract = async (name) => { log.info('fetching', name); const apiHost = getAPIUrl(args); const derivedChainId = args.api !== undefined && args.api !== '' ? getChainId(args.api) : args.chainId; if (apiHost !== undefined && derivedChainId !== undefined && args.network !== undefined && args.networkConfig !== undefined) { const content = await this.retrieveContractFromChain(name, derivedChainId, args.network, args.networkConfig, apiHost); return content || ''; } log.info(`Skipping ${name} as API data or network config is not provided.`); return ''; }; const fs = this.services.filesystem; const files = args.file ? (await Promise.all(args.file.map((file) => fs.readFile(join(process.cwd(), file))))).filter((fileContent) => fileContent !== null) : []; const modules = await pactParser({ contractNames: args.contract, files, getContract, namespace: args.namespace, }); if (args.parseTreePath !== undefined) { await fs.writeFile(args.parseTreePath, JSON.stringify(modules, undefined, 2)); } const moduleDtss = new Map(); Object.keys(modules).forEach((name) => { if (!modules[name].namespace) { log.error(` WARNING: No namespace found for module "${name}".`); } if (!modules[name].functions) { log.error(` WARNING: No functions found for module "${name}". Skipping.`); return; } moduleDtss.set(name, generateDts(modules[name])); }); return moduleDtss; } } //# sourceMappingURL=pactjs.service.js.map