@kadena/kadena-cli
Version:
Kadena CLI tool to interact with the Kadena blockchain (manage keys, transactions, etc.)
153 lines • 7.27 kB
JavaScript
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