From f9520e7f5add28e3d4fa272261e56979db234587 Mon Sep 17 00:00:00 2001 From: Justin D'Errico Date: Tue, 3 May 2022 17:00:24 -0400 Subject: [PATCH 1/3] fix(cli): Smol CLI improvements --- cli/commands/codegen-app.ts | 78 +++++++++++++++++++++--------------- cli/commands/create-app.ts | 79 +++++++++++++++++++++++++++---------- 2 files changed, 105 insertions(+), 52 deletions(-) diff --git a/cli/commands/codegen-app.ts b/cli/commands/codegen-app.ts index a8215b001..bea2d463f 100644 --- a/cli/commands/codegen-app.ts +++ b/cli/commands/codegen-app.ts @@ -1,4 +1,7 @@ +/* eslint no-console: 0 */ + import { Command } from '@oclif/core'; +import chalk from 'chalk'; import dedent from 'dedent'; import { ESLint } from 'eslint'; import fse from 'fs-extra'; @@ -14,47 +17,58 @@ export default class CodegenApp extends Command { static flags = {}; static args = [{ name: 'appId', description: 'The application id ', required: true }]; + private loadDefinition(appId: string) { + try { + return require(`../src/apps/${appId}/${appId}.definition`); + } catch (e) { + throw new Error(`Cannot find app with id "${appId}", please verify you're targeting the right app!`); + } + } async run(): Promise { const { args } = await this.parse(CodegenApp); const appId = args.appId; const appDefinitionName = `${strings.upperCase(appId)}_DEFINITION`; - const definitionModule = require(`../src/apps/${appId}/${appId}.definition`); - const definition = definitionModule[appDefinitionName]; - const networksRaw = definition.supportedNetworks; - - const networks = Object.keys(networksRaw); - const groups = definition.groups; - let moduleImports = ''; - let moduleProviders = ''; - - for (const network of networks) { - moduleImports += generateImportStatementForModule(appId, 'balance', network); - moduleProviders += generateClassNamesForModule(appId, 'balance', network); - - for (const [key, appGroup] of Object.entries(groups)) { - switch (appGroup.type) { - case GroupType.TOKEN: - generateTokenFetcher(appId, key, appGroup.id, network); - moduleImports += generateImportStatementForModule(appId, appGroup.type, network, appGroup.id); - moduleProviders += generateClassNamesForModule(appId, appGroup.type, network, appGroup.id); - break; - - case GroupType.POSITION: - generateContractPosition(appId, key, appGroup.id, network); - moduleImports += generateImportStatementForModule(appId, appGroup.type, network, appGroup.id); - moduleProviders += generateClassNamesForModule(appId, appGroup.type, network, appGroup.id); - break; - - default: - break; + try { + const definitionModule = this.loadDefinition(appId); + const definition = definitionModule[appDefinitionName]; + const networksRaw = definition.supportedNetworks; + + const networks = Object.keys(networksRaw); + const groups = definition.groups; + let moduleImports = ''; + let moduleProviders = ''; + + for (const network of networks) { + moduleImports += generateImportStatementForModule(appId, 'balance', network); + moduleProviders += generateClassNamesForModule(appId, 'balance', network); + + for (const [key, appGroup] of Object.entries(groups)) { + switch (appGroup.type) { + case GroupType.TOKEN: + generateTokenFetcher(appId, key, appGroup.id, network); + moduleImports += generateImportStatementForModule(appId, appGroup.type, network, appGroup.id); + moduleProviders += generateClassNamesForModule(appId, appGroup.type, network, appGroup.id); + break; + + case GroupType.POSITION: + generateContractPosition(appId, key, appGroup.id, network); + moduleImports += generateImportStatementForModule(appId, appGroup.type, network, appGroup.id); + moduleProviders += generateClassNamesForModule(appId, appGroup.type, network, appGroup.id); + break; + + default: + break; + } } + generateBalanceFetcher(appId, network); } - generateBalanceFetcher(appId, network); + await generateModule(appId, moduleImports, moduleProviders); + this.log(`Files for ${appId} were generated !`); + } catch (e) { + console.log(chalk.red(e.message)); } - await generateModule(appId, moduleImports, moduleProviders); - this.log(`Files for ${appId} were generated !`); } } diff --git a/cli/commands/create-app.ts b/cli/commands/create-app.ts index f34c37481..47d390825 100644 --- a/cli/commands/create-app.ts +++ b/cli/commands/create-app.ts @@ -5,6 +5,7 @@ import * as inquirer from 'inquirer'; import { zipObject } from 'lodash'; import prettier from 'prettier'; +import { AppTag } from '../../src/app/app.interface'; import { Network } from '../../src/types/network.interface'; import { strings } from '../strings'; @@ -14,6 +15,42 @@ export default class CreateApp extends Command { static flags = {}; static args = []; + private async promptNetworks() { + let networks: Network[] = []; + do { + const prompt = await inquirer.prompt([ + { + name: 'networks', + message: 'Select (at least one) network supported by the app', + type: 'checkbox', + choices: Object.values(Network) + .filter(v => v !== Network.BITCOIN_MAINNET) + .map(name => ({ name })), + }, + ]); + if (prompt.networks) networks = prompt.networks; + } while (!networks.length); + + return networks; + } + + private async promptTags() { + let tags: AppTag[] = []; + do { + const prompt = await inquirer.prompt([ + { + name: 'tags', + message: 'Select (at least one) tag representing your app', + type: 'checkbox', + choices: Object.values(AppTag).map(name => ({ name })), + }, + ]); + if (prompt.tags) tags = prompt.tags; + } while (!tags.length); + + return tags; + } + async run(): Promise { const appName = await CliUx.ux.prompt('What is the name of the app ', { required: true }); const appId = await CliUx.ux.prompt('What is the ID of the app ', { @@ -23,16 +60,8 @@ export default class CreateApp extends Command { const appDescription = await CliUx.ux.prompt('What is the description of your app ', { required: true }); const appUrl = await CliUx.ux.prompt('What is the URL of your app ', { required: true }); - const { networks } = await inquirer.prompt([ - { - name: 'networks', - message: 'Select networks supported by the app', - type: 'checkbox', - choices: Object.values(Network) - .filter(v => v !== Network.BITCOIN_MAINNET) - .map(name => ({ name })), - }, - ]); + const networks = await this.promptNetworks(); + const tags = await this.promptTags(); createFolder(`./src/apps/${appId}`); createFolder(`./src/apps/${appId}/assets`); @@ -42,7 +71,7 @@ export default class CreateApp extends Command { createFolder(`./src/apps/${appId}/${network}`); } - const generatedCode = generateDefinitionFile(appId, appName, appDescription, appUrl, networks); + const generatedCode = generateDefinitionFile({ appId, appName, appDescription, appUrl, networks, tags }); const config = await prettier.resolveConfig(process.cwd()); fse.writeFileSync( `./src/apps/${appId}/${appId}.definition.ts`, @@ -52,21 +81,31 @@ export default class CreateApp extends Command { } } -function generateDefinitionFile( - appId: string, - appName: string, - appDescription: string, - appUrl: string, - networks: string[], -) { +function generateDefinitionFile({ + appId, + appName, + appDescription, + appUrl, + networks, + tags, +}: { + appId: string; + appName: string; + appDescription: string; + appUrl: string; + networks: Network[]; + tags: AppTag[]; +}) { const appDefinitionName = `${strings.upperCase(appId)}_DEFINITION`; const appClassName = strings.titleCase(appId); + const networkToKey = zipObject(Object.values(Network), Object.keys(Network)); + const tagToKey = zipObject(Object.values(AppTag), Object.keys(AppTag)); return dedent` import { Register } from '~app-toolkit/decorators'; import { AppDefinition } from '~app/app.definition'; - import { AppAction, AppDefinitionObject } from '~app/app.interface'; +import { AppAction, AppTag, AppDefinitionObject } from '~app/app.interface'; import { Network } from '~types/network.interface'; export const ${appDefinitionName}: AppDefinitionObject = { @@ -75,7 +114,7 @@ function generateDefinitionFile( description: '${appDescription}', url: '${appUrl}', groups: {}, - tags: [], + tags: [${tags.map(n => `AppTag.${tagToKey[n]}`).join(',')}], links: { learn: '', github: '', From 8d1228898679aaf123338b6b73346be0c570cb82 Mon Sep 17 00:00:00 2001 From: Justin D'Errico Date: Tue, 3 May 2022 17:02:14 -0400 Subject: [PATCH 2/3] add keywords --- cli/commands/create-app.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/commands/create-app.ts b/cli/commands/create-app.ts index 47d390825..e3ef981ff 100644 --- a/cli/commands/create-app.ts +++ b/cli/commands/create-app.ts @@ -115,6 +115,7 @@ import { AppAction, AppTag, AppDefinitionObject } from '~app/app.interface'; url: '${appUrl}', groups: {}, tags: [${tags.map(n => `AppTag.${tagToKey[n]}`).join(',')}], + keywords: [], links: { learn: '', github: '', From 672ce0b4355db889521cb7dfe98901a80b237d2f Mon Sep 17 00:00:00 2001 From: Justin D'Errico Date: Tue, 3 May 2022 17:02:41 -0400 Subject: [PATCH 3/3] add keywords --- cli/commands/create-app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/commands/create-app.ts b/cli/commands/create-app.ts index e3ef981ff..5a89dbdbb 100644 --- a/cli/commands/create-app.ts +++ b/cli/commands/create-app.ts @@ -105,7 +105,7 @@ function generateDefinitionFile({ return dedent` import { Register } from '~app-toolkit/decorators'; import { AppDefinition } from '~app/app.definition'; -import { AppAction, AppTag, AppDefinitionObject } from '~app/app.interface'; + import { AppAction, AppTag, AppDefinitionObject } from '~app/app.interface'; import { Network } from '~types/network.interface'; export const ${appDefinitionName}: AppDefinitionObject = {