From 26853d7ebdf4d8d3963f0eb890a6ecc7f0bbe0be Mon Sep 17 00:00:00 2001 From: samz Date: Thu, 6 Jun 2024 10:25:32 +0200 Subject: [PATCH] feat: new CLI architecture (part 1) (#1200) --- src/commands/bundle.ts | 14 +- src/commands/config/analytics.ts | 14 +- src/commands/config/context/add.ts | 19 +-- src/commands/config/context/current.ts | 12 +- src/commands/config/context/edit.ts | 13 +- src/commands/config/context/index.ts | 2 +- src/commands/config/context/init.ts | 11 +- src/commands/config/context/list.ts | 12 +- src/commands/config/context/remove.ts | 13 +- src/commands/config/context/use.ts | 13 +- src/commands/config/index.ts | 4 +- src/commands/config/versions.ts | 8 +- src/commands/convert.ts | 19 +-- src/commands/diff.ts | 50 ++---- src/commands/generate/fromTemplate.ts | 70 +------- src/commands/generate/index.ts | 2 +- src/commands/generate/models.ts | 150 +----------------- src/commands/new/file.ts | 23 +-- src/commands/new/glee.ts | 36 +---- src/commands/optimize.ts | 16 +- src/commands/start/index.ts | 4 +- src/commands/start/studio.ts | 14 +- src/commands/validate.ts | 18 +-- src/{ => core}/base.ts | 10 +- src/{ => core}/errors/context-error.ts | 0 src/{ => core}/errors/diff-error.ts | 0 src/{ => core}/errors/generator-error.ts | 0 src/{ => core}/errors/specification-file.ts | 0 src/{ => core}/errors/validation-error.ts | 0 src/core/flags/bundle.flags.ts | 11 ++ src/core/flags/config/analytics.flags.ts | 11 ++ src/core/flags/config/context.flags.ts | 14 ++ src/core/flags/convert.flags.ts | 9 ++ src/core/flags/diff.flags.ts | 35 ++++ src/core/flags/generate/fromTemplate.flags.ts | 60 +++++++ src/core/flags/generate/models.flags.ts | 145 +++++++++++++++++ src/{flags.ts => core/flags/global.flags.ts} | 7 + src/core/flags/new/file.flags.ts | 12 ++ src/core/flags/new/glee.flags.ts | 27 ++++ src/core/flags/optimize.flags.ts | 23 +++ src/core/flags/start/studio.flags.ts | 9 ++ src/core/flags/validate.flags.ts | 11 ++ src/{ => core}/global.d.ts | 0 src/{ => core}/globals.ts | 0 .../hooks/command_not_found/myhook.ts | 0 src/{ => core}/models/Context.ts | 0 src/{ => core}/models/SpecificationFile.ts | 6 +- src/{ => core}/models/Studio.ts | 0 src/{ => core}/parser.ts | 0 src/{ => core}/utils/generator.ts | 0 test/helpers/index.ts | 4 +- test/integration/context.test.ts | 4 +- test/integration/convert.test.ts | 2 +- test/integration/new/glee.test.ts | 8 +- test/integration/validate.test.ts | 2 +- 55 files changed, 514 insertions(+), 433 deletions(-) rename src/{ => core}/base.ts (98%) rename src/{ => core}/errors/context-error.ts (100%) rename src/{ => core}/errors/diff-error.ts (100%) rename src/{ => core}/errors/generator-error.ts (100%) rename src/{ => core}/errors/specification-file.ts (100%) rename src/{ => core}/errors/validation-error.ts (100%) create mode 100644 src/core/flags/bundle.flags.ts create mode 100644 src/core/flags/config/analytics.flags.ts create mode 100644 src/core/flags/config/context.flags.ts create mode 100644 src/core/flags/convert.flags.ts create mode 100644 src/core/flags/diff.flags.ts create mode 100644 src/core/flags/generate/fromTemplate.flags.ts create mode 100644 src/core/flags/generate/models.flags.ts rename src/{flags.ts => core/flags/global.flags.ts} (70%) create mode 100644 src/core/flags/new/file.flags.ts create mode 100644 src/core/flags/new/glee.flags.ts create mode 100644 src/core/flags/optimize.flags.ts create mode 100644 src/core/flags/start/studio.flags.ts create mode 100644 src/core/flags/validate.flags.ts rename src/{ => core}/global.d.ts (100%) rename src/{ => core}/globals.ts (100%) rename src/{ => core}/hooks/command_not_found/myhook.ts (100%) rename src/{ => core}/models/Context.ts (100%) rename src/{ => core}/models/SpecificationFile.ts (99%) rename src/{ => core}/models/Studio.ts (100%) rename src/{ => core}/parser.ts (100%) rename src/{ => core}/utils/generator.ts (100%) diff --git a/src/commands/bundle.ts b/src/commands/bundle.ts index 900ddbd5be0..b1867cd80f8 100644 --- a/src/commands/bundle.ts +++ b/src/commands/bundle.ts @@ -1,10 +1,10 @@ -import { Flags } from '@oclif/core'; -import Command from '../base'; +import Command from '../core/base'; import bundle from '@asyncapi/bundler'; import { promises } from 'fs'; import path from 'path'; -import { Specification } from '../models/SpecificationFile'; +import { Specification } from '../core/models/SpecificationFile'; import { Document } from '@asyncapi/bundler/lib/document'; +import { bundleFlags } from '../core/flags/bundle.flags'; const { writeFile } = promises; @@ -21,13 +21,7 @@ export default class Bundle extends Command { 'asyncapi bundle ./asyncapi.yaml -o final-asyncapi.yaml --base ../public-api/main.yaml --baseDir ./social-media/comments-service', ]; - static flags = { - help: Flags.help({ char: 'h' }), - output: Flags.string({ char: 'o', description: 'The output file name. Omitting this flag the result will be printed in the console.' }), - base: Flags.string({ char: 'b', description: 'Path to the file which will act as a base. This is required when some properties need to be overwritten.' }), - baseDir: Flags.string({ char: 'd', description: 'One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.' }), - xOrigin: Flags.boolean({ char: 'x', description: 'Pass this switch to generate properties "x-origin" that will contain historical values of dereferenced "$ref"s.' }), - }; + static flags = bundleFlags(); async run() { const { argv, flags } = await this.parse(Bundle); diff --git a/src/commands/config/analytics.ts b/src/commands/config/analytics.ts index 7fcb70292a5..3cabf71efeb 100644 --- a/src/commands/config/analytics.ts +++ b/src/commands/config/analytics.ts @@ -1,20 +1,14 @@ -import { Flags } from '@oclif/core'; import { join, resolve } from 'path'; -import Command from '../../base'; +import Command from '../../core/base'; import { promises as fPromises } from 'fs'; import { homedir } from 'os'; +import { analyticsFlags } from '../../core/flags/config/analytics.flags'; const { readFile, writeFile } = fPromises; export default class Analytics extends Command { static readonly description = 'Enable or disable analytics for metrics collection'; - static readonly flags = { - help: Flags.help({ char: 'h' }), - disable: Flags.boolean({ char: 'd', description: 'disable analytics', default: false }), - enable: Flags.boolean({ char: 'e', description: 'enable analytics', default: false }), - status: Flags.boolean({ char: 's', description: 'show current status of analytics' }), - - }; + static readonly flags = analyticsFlags(); async run() { const { flags } = await this.parse(Analytics); @@ -57,5 +51,5 @@ export default class Analytics extends Command { this.error(`Unable to change your analytics configuration. Please check the following message for further info about the error:\n\n${e}`); } } - } + } } diff --git a/src/commands/config/context/add.ts b/src/commands/config/context/add.ts index 3a468c8a91b..f17e19480fd 100644 --- a/src/commands/config/context/add.ts +++ b/src/commands/config/context/add.ts @@ -1,22 +1,15 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../../base'; -import { addContext, setCurrentContext } from '../../../models/Context'; +import { Args } from '@oclif/core'; +import Command from '../../../core/base'; +import { addContext, setCurrentContext } from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { addFlags } from '../../../core/flags/config/context.flags'; export default class ContextAdd extends Command { static description = 'Add a context to the store'; - static flags = { - help: Flags.help({ char: 'h' }), - 'set-current': Flags.boolean({ - char: 's', - description: 'Set context being added as the current context', - default: false, - required: false, - }) - }; + static flags = addFlags(); static args = { 'context-name': Args.string({description: 'context name', required: true}), diff --git a/src/commands/config/context/current.ts b/src/commands/config/context/current.ts index 3227b7a83f3..49067e33dae 100644 --- a/src/commands/config/context/current.ts +++ b/src/commands/config/context/current.ts @@ -1,18 +1,16 @@ -import { Flags } from '@oclif/core'; -import Command from '../../../base'; -import { getCurrentContext, CONTEXT_FILE_PATH } from '../../../models/Context'; +import Command from '../../../core/base'; +import { getCurrentContext, CONTEXT_FILE_PATH } from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, ContextFileEmptyError, ContextNotFoundError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextCurrent extends Command { static description = 'Shows the current context that is being used'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); async run() { let fileContent; diff --git a/src/commands/config/context/edit.ts b/src/commands/config/context/edit.ts index 441c48ea1d7..f16409e9141 100644 --- a/src/commands/config/context/edit.ts +++ b/src/commands/config/context/edit.ts @@ -1,17 +1,16 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../../base'; -import { editContext, CONTEXT_FILE_PATH } from '../../../models/Context'; +import { Args } from '@oclif/core'; +import Command from '../../../core/base'; +import { editContext, CONTEXT_FILE_PATH } from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, ContextFileEmptyError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextEdit extends Command { static description = 'Edit a context in the store'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); static args = { 'context-name': Args.string({description: 'context name', required: true}), diff --git a/src/commands/config/context/index.ts b/src/commands/config/context/index.ts index 0abf5da0b03..3ba767b80a3 100644 --- a/src/commands/config/context/index.ts +++ b/src/commands/config/context/index.ts @@ -1,5 +1,5 @@ import { loadHelpClass } from '@oclif/core'; -import Command from '../../../base'; +import Command from '../../../core/base'; export default class Context extends Command { static description = diff --git a/src/commands/config/context/init.ts b/src/commands/config/context/init.ts index 1ff2d9c9794..40d4dc7ab2c 100644 --- a/src/commands/config/context/init.ts +++ b/src/commands/config/context/init.ts @@ -1,12 +1,11 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../../base'; -import { initContext } from '../../../models/Context'; +import { Args } from '@oclif/core'; +import Command from '../../../core/base'; +import { initContext } from '../../../core/models/Context'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextInit extends Command { static description = 'Initialize context'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); static contextFilePathMessage = `Specify directory in which context file should be created: - current directory : asyncapi config context init . (default) diff --git a/src/commands/config/context/list.ts b/src/commands/config/context/list.ts index 6065476d136..2286173c00c 100644 --- a/src/commands/config/context/list.ts +++ b/src/commands/config/context/list.ts @@ -1,20 +1,18 @@ -import { Flags } from '@oclif/core'; -import Command from '../../../base'; +import Command from '../../../core/base'; import { loadContextFile, isContextFileEmpty, CONTEXT_FILE_PATH, -} from '../../../models/Context'; +} from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextList extends Command { static description = 'List all the stored contexts in the store'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); async run() { try { diff --git a/src/commands/config/context/remove.ts b/src/commands/config/context/remove.ts index c3aaca5c6b7..9a29cd6770d 100644 --- a/src/commands/config/context/remove.ts +++ b/src/commands/config/context/remove.ts @@ -1,17 +1,16 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../../base'; -import { removeContext, CONTEXT_FILE_PATH } from '../../../models/Context'; +import { Args } from '@oclif/core'; +import Command from '../../../core/base'; +import { removeContext, CONTEXT_FILE_PATH } from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, ContextFileEmptyError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextRemove extends Command { static description = 'Delete a context from the store'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); static args = { 'context-name': Args.string({description: 'Name of the context to delete', required: true}), diff --git a/src/commands/config/context/use.ts b/src/commands/config/context/use.ts index ad5e2af657e..6450d524047 100644 --- a/src/commands/config/context/use.ts +++ b/src/commands/config/context/use.ts @@ -1,17 +1,16 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../../base'; -import { setCurrentContext, CONTEXT_FILE_PATH } from '../../../models/Context'; +import { Args } from '@oclif/core'; +import Command from '../../../core/base'; +import { setCurrentContext, CONTEXT_FILE_PATH } from '../../../core/models/Context'; import { MissingContextFileError, ContextFileWrongFormatError, ContextFileEmptyError, -} from '../../../errors/context-error'; +} from '../../../core/errors/context-error'; +import { helpFlag } from '../../../core/flags/global.flags'; export default class ContextUse extends Command { static description = 'Set a context as current'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); static args = { 'context-name': Args.string({description: 'name of the saved context', required: true}), diff --git a/src/commands/config/index.ts b/src/commands/config/index.ts index f90430108de..ecf71751036 100644 --- a/src/commands/config/index.ts +++ b/src/commands/config/index.ts @@ -1,5 +1,5 @@ -import Command from '../../base'; -import {loadHelpClass} from '@oclif/core'; +import Command from '../../core/base'; +import { loadHelpClass } from '@oclif/core'; export default class Config extends Command { static description = 'CLI config settings'; diff --git a/src/commands/config/versions.ts b/src/commands/config/versions.ts index 76ce1953328..915f83ccb3f 100644 --- a/src/commands/config/versions.ts +++ b/src/commands/config/versions.ts @@ -1,12 +1,10 @@ -import { Flags } from '@oclif/core'; -import Command from '../../base'; +import Command from '../../core/base'; +import { helpFlag } from '../../core/flags/global.flags'; export default class Versions extends Command { static description = 'Show versions of AsyncAPI tools used'; - static flags = { - help: Flags.help({ char: 'h' }), - }; + static flags = helpFlag(); async run() { const dependencies: string[] = []; diff --git a/src/commands/convert.ts b/src/commands/convert.ts index 5116304a13f..eb51bf90eae 100644 --- a/src/commands/convert.ts +++ b/src/commands/convert.ts @@ -1,27 +1,24 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { promises as fPromises } from 'fs'; -import { Flags, Args } from '@oclif/core'; -import Command from '../base'; -import { ValidationError } from '../errors/validation-error'; -import { load } from '../models/SpecificationFile'; -import { SpecificationFileNotFound } from '../errors/specification-file'; +import { Args } from '@oclif/core'; +import Command from '../core/base'; +import { ValidationError } from '../core/errors/validation-error'; +import { load } from '../core/models/SpecificationFile'; +import { SpecificationFileNotFound } from '../core/errors/specification-file'; import { convert } from '@asyncapi/converter'; import type { ConvertVersion } from '@asyncapi/converter'; import { cyan, green } from 'picocolors'; // @ts-ignore import specs from '@asyncapi/specs'; +import { convertFlags } from '../core/flags/convert.flags'; const latestVersion = Object.keys(specs.schemas).pop() as string; export default class Convert extends Command { static description = 'Convert asyncapi documents older to newer versions'; - static flags = { - help: Flags.help({ char: 'h' }), - output: Flags.string({ char: 'o', description: 'path to the file where the result is saved' }), - 'target-version': Flags.string({ char: 't', description: 'asyncapi version to convert to', default: latestVersion }) - }; + static flags = convertFlags(latestVersion); static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), @@ -71,6 +68,6 @@ export default class Convert extends Command { } else { this.error(err as Error); } - } + } } } diff --git a/src/commands/diff.ts b/src/commands/diff.ts index 078825f56cb..4ce8ae431bc 100644 --- a/src/commands/diff.ts +++ b/src/commands/diff.ts @@ -1,58 +1,30 @@ /* eslint-disable sonarjs/no-duplicate-string */ -import { Flags, Args } from '@oclif/core'; +import { Args } from '@oclif/core'; import * as diff from '@asyncapi/diff'; import AsyncAPIDiff from '@asyncapi/diff/lib/asyncapidiff'; import { promises as fs } from 'fs'; import chalk from 'chalk'; -import { load, Specification } from '../models/SpecificationFile'; -import Command from '../base'; -import { ValidationError } from '../errors/validation-error'; -import { SpecificationFileNotFound } from '../errors/specification-file'; +import { load, Specification } from '../core/models/SpecificationFile'; +import Command from '../core/base'; +import { ValidationError } from '../core/errors/validation-error'; +import { SpecificationFileNotFound } from '../core/errors/specification-file'; import { DiffBreakingChangeError, DiffOverrideFileError, DiffOverrideJSONError, -} from '../errors/diff-error'; -import { specWatcher } from '../globals'; -import { watchFlag } from '../flags'; -import { validationFlags, parse, convertToOldAPI } from '../parser'; +} from '../core/errors/diff-error'; +import { specWatcher } from '../core/globals'; +import { parse, convertToOldAPI } from '../core/parser'; -import type { SpecWatcherParams } from '../globals'; +import type { SpecWatcherParams } from '../core/globals'; +import { diffFlags } from '../core/flags/diff.flags'; const { readFile } = fs; export default class Diff extends Command { static description = 'Find diff between two asyncapi files'; - static flags = { - help: Flags.help({ char: 'h' }), - format: Flags.string({ - char: 'f', - description: 'format of the output', - default: 'yaml', - options: ['json', 'yaml', 'yml', 'md'], - }), - type: Flags.string({ - char: 't', - description: 'type of the output', - default: 'all', - options: ['breaking', 'non-breaking', 'unclassified', 'all'], - }), - markdownSubtype: Flags.string({ - description: 'the format of changes made to AsyncAPI document. It works only when diff is generated using md type. For example, when you specify subtype as json, then diff information in markdown is dumped as json structure.', - default: undefined, - options: ['json', 'yaml', 'yml'] - }), - overrides: Flags.string({ - char: 'o', - description: 'path to JSON file containing the override properties', - }), - 'no-error': Flags.boolean({ - description: 'don\'t show error on breaking changes', - }), - watch: watchFlag(), - ...validationFlags({ logDiagnostics: false }), - }; + static flags = diffFlags(); static args = { old: Args.string({description: 'old spec path, URL or context-name', required: true}), diff --git a/src/commands/generate/fromTemplate.ts b/src/commands/generate/fromTemplate.ts index 6ebda661a3f..58b0d782391 100644 --- a/src/commands/generate/fromTemplate.ts +++ b/src/commands/generate/fromTemplate.ts @@ -1,20 +1,20 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../../base'; +import { Args } from '@oclif/core'; +import Command from '../../core/base'; // eslint-disable-next-line // @ts-ignore import AsyncAPIGenerator from '@asyncapi/generator'; import path from 'path'; import os from 'os'; import fs from 'fs'; -import { load, Specification } from '../../models/SpecificationFile'; -import { watchFlag } from '../../flags'; -import { isLocalTemplate, Watcher } from '../../utils/generator'; -import { ValidationError } from '../../errors/validation-error'; -import { GeneratorError } from '../../errors/generator-error'; +import { load, Specification } from '../../core/models/SpecificationFile'; +import { isLocalTemplate, Watcher } from '../../core/utils/generator'; +import { ValidationError } from '../../core/errors/validation-error'; +import { GeneratorError } from '../../core/errors/generator-error'; import { Parser } from '@asyncapi/parser'; import { intro, isCancel, spinner, text } from '@clack/prompts'; import { inverse, yellow, magenta, green, red } from 'picocolors'; import fetch from 'node-fetch'; +import { fromTemplateFlags } from '../../core/flags/generate/fromTemplate.flags'; interface IMapBaseUrlToFlag { url: string, @@ -56,61 +56,7 @@ export default class Template extends Command { 'asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template --param version=1.0.0 singleFile=true --output ./docs --force-write' ]; - static flags = { - help: Flags.help({ char: 'h' }), - 'disable-hook': Flags.string({ - char: 'd', - description: 'Disable a specific hook type or hooks from a given hook type', - multiple: true - }), - 'no-interactive': Flags.boolean({ - description: 'Disable interactive mode and run with the provided flags.', - required: false, - default: false, - }), - install: Flags.boolean({ - char: 'i', - default: false, - description: 'Installs the template and its dependencies (defaults to false)' - }), - debug: Flags.boolean({ - description: 'Enable more specific errors in the console' - }), - 'no-overwrite': Flags.string({ - char: 'n', - multiple: true, - description: 'Glob or path of the file(s) to skip when regenerating' - }), - output: Flags.string({ - char: 'o', - description: 'Directory where to put the generated files (defaults to current directory)', - }), - 'force-write': Flags.boolean({ - default: false, - description: 'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)' - }), - watch: watchFlag( - 'Watches the template directory and the AsyncAPI document, and re-generate the files when changes occur. Ignores the output directory.' - ), - param: Flags.string({ - char: 'p', - description: 'Additional param to pass to templates', - multiple: true - }), - 'map-base-url': Flags.string({ - description: 'Maps all schema references from base url to local folder' - }), - 'registry-url': Flags.string({ - default: 'https://registry.npmjs.org', - description: 'Specifies the URL of the private registry for fetching templates and dependencies' - }), - 'registry-auth': Flags.string({ - description: 'The registry username and password encoded with base64, formatted as username:password' - }), - 'registry-token': Flags.string({ - description: 'The npm registry authentication token, that can be passed instead of base64 encoded username and password' - }) - }; + static flags = fromTemplateFlags(); static args = { asyncapi: Args.string({description: '- Local path, url or context-name pointing to AsyncAPI file', required: true}), diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index fd719df01ee..643bca0df0c 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -1,4 +1,4 @@ -import Command from '../../base'; +import Command from '../../core/base'; import { Help } from '@oclif/core'; export default class Generate extends Command { diff --git a/src/commands/generate/models.ts b/src/commands/generate/models.ts index 25e2a9afdf5..5a8d853efeb 100644 --- a/src/commands/generate/models.ts +++ b/src/commands/generate/models.ts @@ -1,14 +1,15 @@ import { CSHARP_COMMON_PRESET, CSHARP_DEFAULT_PRESET, CSHARP_JSON_SERIALIZER_PRESET, CSHARP_NEWTONSOFT_SERIALIZER_PRESET, CSharpFileGenerator, CplusplusFileGenerator, DartFileGenerator, GoFileGenerator, JAVA_COMMON_PRESET, JAVA_CONSTRAINTS_PRESET, JAVA_DESCRIPTION_PRESET, JAVA_JACKSON_PRESET, JavaFileGenerator, JavaScriptFileGenerator, KotlinFileGenerator, Logger, PhpFileGenerator, PythonFileGenerator, RustFileGenerator, TS_COMMON_PRESET, TS_DESCRIPTION_PRESET, TS_JSONBINPACK_PRESET, TypeScriptFileGenerator } from '@asyncapi/modelina'; -import { Args, Flags } from '@oclif/core'; +import { Args } from '@oclif/core'; import { ConvertDocumentParserAPIVersion } from '@smoya/multi-parser'; -import Command from '../../base'; -import { load } from '../../models/SpecificationFile'; -import { ValidateOptions, formatOutput, parse, validationFlags } from '../../parser'; +import Command from '../../core/base'; +import { load } from '../../core/models/SpecificationFile'; +import { ValidateOptions, formatOutput, parse } from '../../core/parser'; import { cancel, intro, isCancel, select, spinner, text } from '@clack/prompts'; import { green, inverse } from 'picocolors'; import type { AbstractFileGenerator, AbstractGenerator } from '@asyncapi/modelina'; +import { modelsFlags } from '../../core/flags/generate/models.flags'; enum Languages { typescript = 'typescript', @@ -33,146 +34,7 @@ export default class Models extends Command { file: Args.string({description: 'Path or URL to the AsyncAPI document, or context-name', required: true}), }; - static flags = { - help: Flags.help({ char: 'h' }), - 'no-interactive': Flags.boolean({ - description: 'Disable interactive mode and run with the provided flags.', - required: false, - default: false, - }), - output: Flags.string({ - char: 'o', - description: 'The output directory where the models should be written to. Omitting this flag will write the models to `stdout`.', - required: false - }), - /** - * TypeScript specific options - */ - tsModelType: Flags.string({ - type: 'option', - options: ['class', 'interface'], - description: 'TypeScript specific, define which type of model needs to be generated.', - required: false, - default: 'class', - }), - tsEnumType: Flags.string({ - type: 'option', - options: ['enum', 'union'], - description: 'TypeScript specific, define which type of enums needs to be generated.', - required: false, - default: 'enum', - }), - tsModuleSystem: Flags.string({ - type: 'option', - options: ['ESM', 'CJS'], - description: 'TypeScript specific, define the module system to be used.', - required: false, - default: 'ESM', - - }), - tsIncludeComments: Flags.boolean({ - description: 'TypeScript specific, if enabled add comments while generating models.', - required: false, - default: false, - }), - tsExportType: Flags.string({ - type: 'option', - options: ['default', 'named'], - description: 'TypeScript specific, define which type of export needs to be generated.', - required: false, - default: 'default', - }), - tsJsonBinPack: Flags.boolean({ - description: 'TypeScript specific, define basic support for serializing to and from binary with jsonbinpack.', - required: false, - default: false, - }), - tsMarshalling: Flags.boolean({ - description: 'TypeScript specific, generate the models with marshalling functions.', - required: false, - default: false, - }), - tsExampleInstance: Flags.boolean({ - description: 'Typescript specific, generate example of the model', - required: false, - default: false, - }), - tsRawPropertyNames: Flags.boolean({ - description: 'Typescript specific, generate the models using raw property names.', - required: false, - default: false, - }), - /** - * Go and Java specific package name to use for the generated models - */ - packageName: Flags.string({ - description: 'Go, Java and Kotlin specific, define the package to use for the generated models. This is required when language is `go`, `java` or `kotlin`.', - required: false - }), - /** - * Java specific options - */ - javaIncludeComments: Flags.boolean({ - description: 'Java specific, if enabled add comments while generating models.', - required: false, - default: false - }), - javaJackson: Flags.boolean({ - description: 'Java specific, generate the models with Jackson serialization support', - required: false, - default: false - }), - javaConstraints: Flags.boolean({ - description: 'Java specific, generate the models with constraints', - required: false, - default: false - }), - - /** - * C++ and C# and PHP specific namespace to use for the generated models - */ - namespace: Flags.string({ - description: 'C#, C++ and PHP specific, define the namespace to use for the generated models. This is required when language is `csharp`,`c++` or `php`.', - required: false - }), - - /** - * C# specific options - */ - csharpAutoImplement: Flags.boolean({ - description: 'C# specific, define whether to generate auto-implemented properties or not.', - required: false, - default: false - }), - csharpNewtonsoft: Flags.boolean({ - description: 'C# specific, generate the models with newtonsoft serialization support', - required: false, - default: false - }), - csharpArrayType: Flags.string({ - type: 'option', - description: 'C# specific, define which type of array needs to be generated.', - options: ['Array', 'List'], - required: false, - default: 'Array' - }), - csharpHashcode: Flags.boolean({ - description: 'C# specific, generate the models with the GetHashCode method overwritten', - required: false, - default: false - }), - csharpEqual: Flags.boolean({ - description: 'C# specific, generate the models with the Equal method overwritten', - required: false, - default: false - }), - csharpSystemJson: Flags.boolean({ - description: 'C# specific, generate the models with System.Text.Json serialization support', - required: false, - default: false - }), - ...validationFlags({ logDiagnostics: false }), - }; + static flags = modelsFlags(); /* eslint-disable sonarjs/cognitive-complexity */ async run() { diff --git a/src/commands/new/file.ts b/src/commands/new/file.ts index 52964b66763..ea5c5560a5f 100644 --- a/src/commands/new/file.ts +++ b/src/commands/new/file.ts @@ -1,11 +1,11 @@ -import {Flags} from '@oclif/core'; import { promises as fPromises, readFileSync } from 'fs'; -import Command from '../../base'; +import Command from '../../core/base'; import * as inquirer from 'inquirer'; -import { start as startStudio, DEFAULT_PORT } from '../../models/Studio'; +import { start as startStudio, DEFAULT_PORT } from '../../core/models/Studio'; import { resolve } from 'path'; -import { load } from '../../models/SpecificationFile'; +import { load } from '../../core/models/SpecificationFile'; import { cyan } from 'picocolors'; +import { fileFlags } from '../../core/flags/new/file.flags'; const { writeFile, readFile } = fPromises; const DEFAULT_ASYNCAPI_FILE_NAME = 'asyncapi.yaml'; @@ -33,15 +33,8 @@ function getExamplesFlagDescription (): string { export default class NewFile extends Command { static description = 'Creates a new asyncapi file'; - static flags = { - help: Flags.help({ char: 'h' }), - 'file-name': Flags.string({ char: 'n', description: 'name of the file' }), - example: Flags.string({ char: 'e', description: getExamplesFlagDescription() }), - studio: Flags.boolean({ char: 's', description: 'open in Studio' }), - port: Flags.integer({ char: 'p', description: 'port in which to start Studio' }), - 'no-tty': Flags.boolean({ description: 'do not use an interactive terminal' }), - }; - + static flags = fileFlags(getExamplesFlagDescription()); + static examples = [ 'asyncapi new\t - start creation of a file in interactive mode', 'asyncapi new --file-name=my-asyncapi.yml --example=default-example.yml --no-tty\t - create a new file with a specific name, using one of the examples and without interactive mode' @@ -128,7 +121,7 @@ export default class NewFile extends Command { if (!fileName) {fileName = answers.filename as string;} if (!selectedTemplate) {selectedTemplate = answers.selectedTemplate as string;} if (openStudio === undefined) {openStudio = answers.studio;} - } + } fileName = fileName || DEFAULT_ASYNCAPI_FILE_NAME; selectedTemplate = selectedTemplate || DEFAULT_ASYNCAPI_TEMPLATE; @@ -141,7 +134,7 @@ export default class NewFile extends Command { const asyncApiFile = await readFile(resolve(__dirname, '../../../assets/examples/', selectedTemplate), { encoding: 'utf8' }); let fileNameToWriteToDisk; - + if (!fileName.includes('.')) { fileNameToWriteToDisk=`${fileName}.yaml`; } else { diff --git a/src/commands/new/glee.ts b/src/commands/new/glee.ts index 107fb55e631..6ad9a04fa0f 100644 --- a/src/commands/new/glee.ts +++ b/src/commands/new/glee.ts @@ -1,15 +1,15 @@ -import { Flags } from '@oclif/core'; import { promises as fPromises } from 'fs'; -import Command from '../../base'; +import Command from '../../core/base'; import path, { resolve, join } from 'path'; import fs from 'fs-extra'; -import { Specification, load } from '../../models/SpecificationFile'; +import { Specification, load } from '../../core/models/SpecificationFile'; import yaml from 'js-yaml'; import { prompt } from 'inquirer'; // eslint-disable-next-line // @ts-ignore import Generator from '@asyncapi/generator'; import { cyan, gray } from 'picocolors'; +import { gleeFlags } from '../../core/flags/new/glee.flags'; export const successMessage = (projectName: string) => `🎉 Your Glee project has been successfully created! @@ -17,17 +17,17 @@ export const successMessage = (projectName: string) => cd ${projectName}\t\t ${gray('# Navigate to the project directory')} npm install\t\t ${gray('# Install the project dependencies')} - npm run dev\t\t ${gray('# Start the project in development mode')} + npm run dev\t\t ${gray('# Start the project in development mode')} You can also open the project in your favourite editor and start tweaking it. `; const errorMessages = { alreadyExists: (projectName: string) => - `Unable to create the project because the directory "${cyan(projectName)}" already exists at "${process.cwd()}/${projectName}". + `Unable to create the project because the directory "${cyan(projectName)}" already exists at "${process.cwd()}/${projectName}". To specify a different name for the new project, please run the command below with a unique project name: - ${gray('asyncapi new glee --name ') + gray(projectName) + gray('-1')}`, + ${gray('asyncapi new glee --name ') + gray(projectName) + gray('-1')}`, }; export default class NewGlee extends Command { @@ -36,29 +36,7 @@ export default class NewGlee extends Command { static readonly successMessage = successMessage; static readonly errorMessages = errorMessages; - static flags = { - help: Flags.help({ char: 'h' }), - name: Flags.string({ - char: 'n', - description: 'Name of the Project', - default: 'project', - }), - template: Flags.string({ - char: 't', - description: 'Name of the Template', - default: 'default', - }), - file: Flags.string({ - char: 'f', - description: - 'The path to the AsyncAPI file for generating a Glee project.', - }), - 'force-write': Flags.boolean({ - default: false, - description: - 'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)', - }), - }; + static flags = gleeFlags(); async getFilteredServers(serversObject: any) { console.log({ serversObject }); diff --git a/src/commands/optimize.ts b/src/commands/optimize.ts index 63af46a680c..d5e81f4e499 100644 --- a/src/commands/optimize.ts +++ b/src/commands/optimize.ts @@ -1,12 +1,13 @@ -import { Flags, Args } from '@oclif/core'; +import { Args } from '@oclif/core'; import { Optimizer, Output, Report, ReportElement } from '@asyncapi/optimizer'; -import Command from '../base'; -import { ValidationError } from '../errors/validation-error'; -import { load } from '../models/SpecificationFile'; +import Command from '../core/base'; +import { ValidationError } from '../core/errors/validation-error'; +import { load } from '../core/models/SpecificationFile'; import * as inquirer from 'inquirer'; import chalk from 'chalk'; import { promises } from 'fs'; import { Parser } from '@asyncapi/parser'; +import { optimizeFlags } from '../core/flags/optimize.flags'; const { writeFile } = promises; @@ -35,12 +36,7 @@ export default class Optimize extends Command { 'asyncapi optimize ./asyncapi.yaml --optimization=remove-components --output=terminal --no-tty', ]; - static flags = { - help: Flags.help({ char: 'h' }), - optimization: Flags.string({char: 'p', default: Object.values(Optimizations), options: Object.values(Optimizations), multiple: true, description: 'select the type of optimizations that you want to apply.'}), - output: Flags.string({char: 'o', default: Outputs.TERMINAL, options: Object.values(Outputs), description: 'select where you want the output.'}), - 'no-tty': Flags.boolean({ description: 'do not use an interactive terminal', default: false }), - }; + static flags = optimizeFlags(); static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), diff --git a/src/commands/start/index.ts b/src/commands/start/index.ts index 545ef5b82ab..8c75ce39b69 100644 --- a/src/commands/start/index.ts +++ b/src/commands/start/index.ts @@ -1,8 +1,8 @@ -import Command from '../../base'; +import Command from '../../core/base'; import { Help } from '@oclif/core'; export default class Start extends Command { - static description = 'Start asyncapi studio'; + static description = ''; async run() { const help = new Help(this.config); help.showHelp(['start', '--help']); diff --git a/src/commands/start/studio.ts b/src/commands/start/studio.ts index 1c459f06195..23bc3e42e28 100644 --- a/src/commands/start/studio.ts +++ b/src/commands/start/studio.ts @@ -1,16 +1,12 @@ -import {Flags} from '@oclif/core'; -import Command from '../../base'; -import { start as startStudio } from '../../models/Studio'; -import { load } from '../../models/SpecificationFile'; +import Command from '../../core/base'; +import { start as startStudio } from '../../core/models/Studio'; +import { load } from '../../core/models/SpecificationFile'; +import { studioFlags } from '../../core/flags/start/studio.flags'; export default class StartStudio extends Command { static description = 'starts a new local instance of Studio'; - static flags = { - help: Flags.help({ char: 'h' }), - file: Flags.string({ char: 'f', description: 'path to the AsyncAPI file to link with Studio' }), - port: Flags.integer({ char: 'p', description: 'port in which to start Studio' }), - }; + static flags = studioFlags(); async run() { const { flags } = await this.parse(StartStudio); diff --git a/src/commands/validate.ts b/src/commands/validate.ts index 7291fd69173..fb3b32d6df4 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -1,18 +1,14 @@ -import { Flags, Args } from '@oclif/core'; -import Command from '../base'; -import { validate, validationFlags, ValidateOptions, ValidationStatus } from '../parser'; -import { load } from '../models/SpecificationFile'; -import { specWatcher } from '../globals'; -import { watchFlag } from '../flags'; +import { Args } from '@oclif/core'; +import Command from '../core/base'; +import { validate, ValidateOptions, ValidationStatus } from '../core/parser'; +import { load } from '../core/models/SpecificationFile'; +import { specWatcher } from '../core/globals'; +import { validateFlags } from '../core/flags/validate.flags'; export default class Validate extends Command { static description = 'validate asyncapi file'; - static flags = { - help: Flags.help({ char: 'h' }), - watch: watchFlag(), - ...validationFlags(), - }; + static flags = validateFlags(); static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), diff --git a/src/base.ts b/src/core/base.ts similarity index 98% rename from src/base.ts rename to src/core/base.ts index 3398df58c74..62d252366d4 100644 --- a/src/base.ts +++ b/src/core/base.ts @@ -1,7 +1,7 @@ import { Command } from '@oclif/core'; import { MetadataFromDocument, MetricMetadata, NewRelicSink, Recorder, Sink, StdOutSink } from '@smoya/asyncapi-adoption-metrics'; import { Parser } from '@asyncapi/parser'; -import { Specification } from 'models/SpecificationFile'; +import { Specification } from './models/SpecificationFile'; import { join, resolve } from 'path'; import { existsSync } from 'fs-extra'; import { promises as fPromises } from 'fs'; @@ -52,7 +52,7 @@ export default abstract class extends Command { } } } - + const callable = async function(recorder: Recorder) { await recorder.recordActionFinished(action, metadata); }; @@ -95,7 +95,7 @@ export default abstract class extends Command { this.metricsMetadata['success'] = error === undefined; await this.recordActionFinished(this.id as string, this.metricsMetadata, this.specFile?.text()); } - + async recorderFromEnv(prefix: string): Promise { let sink: Sink = new DiscardSink(); const analyticsConfigFile = process.env.ASYNCAPI_METRICS_CONFIG_PATH || join(homedir(), '.asyncapi-analytics'); @@ -124,11 +124,11 @@ export default abstract class extends Command { this.log('\nAsyncAPI anonymously tracks command executions to improve the specification and tools, ensuring no sensitive data reaches our servers. It aids in comprehending how AsyncAPI tools are used and adopted, facilitating ongoing improvements to our specifications and tools.\n\nTo disable tracking, please run the following command:\n asyncapi config analytics --disable\n\nOnce disabled, if you want to enable tracking back again then run:\n asyncapi config analytics --enable\n'); analyticsConfigFileContent.infoMessageShown = 'true'; await writeFile(analyticsConfigFile, JSON.stringify(analyticsConfigFileContent), { encoding: 'utf8' }); - } + } break; } } - + return new Recorder(prefix, sink); } } diff --git a/src/errors/context-error.ts b/src/core/errors/context-error.ts similarity index 100% rename from src/errors/context-error.ts rename to src/core/errors/context-error.ts diff --git a/src/errors/diff-error.ts b/src/core/errors/diff-error.ts similarity index 100% rename from src/errors/diff-error.ts rename to src/core/errors/diff-error.ts diff --git a/src/errors/generator-error.ts b/src/core/errors/generator-error.ts similarity index 100% rename from src/errors/generator-error.ts rename to src/core/errors/generator-error.ts diff --git a/src/errors/specification-file.ts b/src/core/errors/specification-file.ts similarity index 100% rename from src/errors/specification-file.ts rename to src/core/errors/specification-file.ts diff --git a/src/errors/validation-error.ts b/src/core/errors/validation-error.ts similarity index 100% rename from src/errors/validation-error.ts rename to src/core/errors/validation-error.ts diff --git a/src/core/flags/bundle.flags.ts b/src/core/flags/bundle.flags.ts new file mode 100644 index 00000000000..370df930a4f --- /dev/null +++ b/src/core/flags/bundle.flags.ts @@ -0,0 +1,11 @@ +import { Flags } from '@oclif/core'; + +export const bundleFlags = () => { + return { + help: Flags.help({ char: 'h' }), + output: Flags.string({ char: 'o', description: 'The output file name. Omitting this flag the result will be printed in the console.' }), + base: Flags.string({ char: 'b', description: 'Path to the file which will act as a base. This is required when some properties need to be overwritten.' }), + baseDir: Flags.string({ char: 'd', description: 'One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.' }), + xOrigin: Flags.boolean({ char: 'x', description: 'Pass this switch to generate properties "x-origin" that will contain historical values of dereferenced "$ref"s.' }), + }; +}; diff --git a/src/core/flags/config/analytics.flags.ts b/src/core/flags/config/analytics.flags.ts new file mode 100644 index 00000000000..b2bac4a2c82 --- /dev/null +++ b/src/core/flags/config/analytics.flags.ts @@ -0,0 +1,11 @@ +import { Flags } from '@oclif/core'; + +export const analyticsFlags = () => { + return { + help: Flags.help({ char: 'h' }), + disable: Flags.boolean({ char: 'd', description: 'disable analytics', default: false }), + enable: Flags.boolean({ char: 'e', description: 'enable analytics', default: false }), + status: Flags.boolean({ char: 's', description: 'show current status of analytics' }), + }; +}; + diff --git a/src/core/flags/config/context.flags.ts b/src/core/flags/config/context.flags.ts new file mode 100644 index 00000000000..ce8db51ce12 --- /dev/null +++ b/src/core/flags/config/context.flags.ts @@ -0,0 +1,14 @@ +import { Flags } from '@oclif/core'; + +export const addFlags = () => { + return { + help: Flags.help({ char: 'h' }), + 'set-current': Flags.boolean({ + char: 's', + description: 'Set context being added as the current context', + default: false, + required: false, + }) + }; +}; + diff --git a/src/core/flags/convert.flags.ts b/src/core/flags/convert.flags.ts new file mode 100644 index 00000000000..98c7bd2c7e9 --- /dev/null +++ b/src/core/flags/convert.flags.ts @@ -0,0 +1,9 @@ +import { Flags } from '@oclif/core'; + +export const convertFlags = (latestVersion: string) => { + return { + help: Flags.help({ char: 'h' }), + output: Flags.string({ char: 'o', description: 'path to the file where the result is saved' }), + 'target-version': Flags.string({ char: 't', description: 'asyncapi version to convert to', default: latestVersion }) + }; +}; diff --git a/src/core/flags/diff.flags.ts b/src/core/flags/diff.flags.ts new file mode 100644 index 00000000000..ca29bc54c44 --- /dev/null +++ b/src/core/flags/diff.flags.ts @@ -0,0 +1,35 @@ +import { Flags } from '@oclif/core'; +import { validationFlags } from '../parser'; +import { watchFlag } from './global.flags'; + +export const diffFlags = () => { + return { + help: Flags.help({ char: 'h' }), + format: Flags.string({ + char: 'f', + description: 'format of the output', + default: 'yaml', + options: ['json', 'yaml', 'yml', 'md'], + }), + type: Flags.string({ + char: 't', + description: 'type of the output', + default: 'all', + options: ['breaking', 'non-breaking', 'unclassified', 'all'], + }), + markdownSubtype: Flags.string({ + description: 'the format of changes made to AsyncAPI document. It works only when diff is generated using md type. For example, when you specify subtype as json, then diff information in markdown is dumped as json structure.', + default: undefined, + options: ['json', 'yaml', 'yml'] + }), + overrides: Flags.string({ + char: 'o', + description: 'path to JSON file containing the override properties', + }), + 'no-error': Flags.boolean({ + description: 'don\'t show error on breaking changes', + }), + watch: watchFlag(), + ...validationFlags({ logDiagnostics: false }), + }; +}; diff --git a/src/core/flags/generate/fromTemplate.flags.ts b/src/core/flags/generate/fromTemplate.flags.ts new file mode 100644 index 00000000000..41f550b5e28 --- /dev/null +++ b/src/core/flags/generate/fromTemplate.flags.ts @@ -0,0 +1,60 @@ +import { Flags } from '@oclif/core'; +import { watchFlag } from '../global.flags'; + +export const fromTemplateFlags = () => { + return { + help: Flags.help({ char: 'h' }), + 'disable-hook': Flags.string({ + char: 'd', + description: 'Disable a specific hook type or hooks from a given hook type', + multiple: true + }), + 'no-interactive': Flags.boolean({ + description: 'Disable interactive mode and run with the provided flags.', + required: false, + default: false, + }), + install: Flags.boolean({ + char: 'i', + default: false, + description: 'Installs the template and its dependencies (defaults to false)' + }), + debug: Flags.boolean({ + description: 'Enable more specific errors in the console' + }), + 'no-overwrite': Flags.string({ + char: 'n', + multiple: true, + description: 'Glob or path of the file(s) to skip when regenerating' + }), + output: Flags.string({ + char: 'o', + description: 'Directory where to put the generated files (defaults to current directory)', + }), + 'force-write': Flags.boolean({ + default: false, + description: 'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)' + }), + watch: watchFlag( + 'Watches the template directory and the AsyncAPI document, and re-generate the files when changes occur. Ignores the output directory.' + ), + param: Flags.string({ + char: 'p', + description: 'Additional param to pass to templates', + multiple: true + }), + 'map-base-url': Flags.string({ + description: 'Maps all schema references from base url to local folder' + }), + 'registry-url': Flags.string({ + default: 'https://registry.npmjs.org', + description: 'Specifies the URL of the private registry for fetching templates and dependencies' + }), + 'registry-auth': Flags.string({ + description: 'The registry username and password encoded with base64, formatted as username:password' + }), + 'registry-token': Flags.string({ + description: 'The npm registry authentication token, that can be passed instead of base64 encoded username and password' + }) + }; +}; diff --git a/src/core/flags/generate/models.flags.ts b/src/core/flags/generate/models.flags.ts new file mode 100644 index 00000000000..ff85b28b22f --- /dev/null +++ b/src/core/flags/generate/models.flags.ts @@ -0,0 +1,145 @@ +import { Flags } from '@oclif/core'; +import { validationFlags } from '../../parser'; + +export const modelsFlags = () => { + return { + help: Flags.help({ char: 'h' }), + 'no-interactive': Flags.boolean({ + description: 'Disable interactive mode and run with the provided flags.', + required: false, + default: false, + }), + output: Flags.string({ + char: 'o', + description: 'The output directory where the models should be written to. Omitting this flag will write the models to `stdout`.', + required: false + }), + /** + * TypeScript specific options + */ + tsModelType: Flags.string({ + type: 'option', + options: ['class', 'interface'], + description: 'TypeScript specific, define which type of model needs to be generated.', + required: false, + default: 'class', + }), + tsEnumType: Flags.string({ + type: 'option', + options: ['enum', 'union'], + description: 'TypeScript specific, define which type of enums needs to be generated.', + required: false, + default: 'enum', + }), + tsModuleSystem: Flags.string({ + type: 'option', + options: ['ESM', 'CJS'], + description: 'TypeScript specific, define the module system to be used.', + required: false, + default: 'ESM', + + }), + tsIncludeComments: Flags.boolean({ + description: 'TypeScript specific, if enabled add comments while generating models.', + required: false, + default: false, + }), + tsExportType: Flags.string({ + type: 'option', + options: ['default', 'named'], + description: 'TypeScript specific, define which type of export needs to be generated.', + required: false, + default: 'default', + }), + tsJsonBinPack: Flags.boolean({ + description: 'TypeScript specific, define basic support for serializing to and from binary with jsonbinpack.', + required: false, + default: false, + }), + tsMarshalling: Flags.boolean({ + description: 'TypeScript specific, generate the models with marshalling functions.', + required: false, + default: false, + }), + tsExampleInstance: Flags.boolean({ + description: 'Typescript specific, generate example of the model', + required: false, + default: false, + }), + tsRawPropertyNames: Flags.boolean({ + description: 'Typescript specific, generate the models using raw property names.', + required: false, + default: false, + }), + /** + * Go and Java specific package name to use for the generated models + */ + packageName: Flags.string({ + description: 'Go, Java and Kotlin specific, define the package to use for the generated models. This is required when language is `go`, `java` or `kotlin`.', + required: false + }), + /** + * Java specific options + */ + javaIncludeComments: Flags.boolean({ + description: 'Java specific, if enabled add comments while generating models.', + required: false, + default: false + }), + javaJackson: Flags.boolean({ + description: 'Java specific, generate the models with Jackson serialization support', + required: false, + default: false + }), + javaConstraints: Flags.boolean({ + description: 'Java specific, generate the models with constraints', + required: false, + default: false + }), + + /** + * C++ and C# and PHP specific namespace to use for the generated models + */ + namespace: Flags.string({ + description: 'C#, C++ and PHP specific, define the namespace to use for the generated models. This is required when language is `csharp`,`c++` or `php`.', + required: false + }), + + /** + * C# specific options + */ + csharpAutoImplement: Flags.boolean({ + description: 'C# specific, define whether to generate auto-implemented properties or not.', + required: false, + default: false + }), + csharpNewtonsoft: Flags.boolean({ + description: 'C# specific, generate the models with newtonsoft serialization support', + required: false, + default: false + }), + csharpArrayType: Flags.string({ + type: 'option', + description: 'C# specific, define which type of array needs to be generated.', + options: ['Array', 'List'], + required: false, + default: 'Array' + }), + csharpHashcode: Flags.boolean({ + description: 'C# specific, generate the models with the GetHashCode method overwritten', + required: false, + default: false + }), + csharpEqual: Flags.boolean({ + description: 'C# specific, generate the models with the Equal method overwritten', + required: false, + default: false + }), + csharpSystemJson: Flags.boolean({ + description: 'C# specific, generate the models with System.Text.Json serialization support', + required: false, + default: false + }), + ...validationFlags({ logDiagnostics: false }), + }; +}; diff --git a/src/flags.ts b/src/core/flags/global.flags.ts similarity index 70% rename from src/flags.ts rename to src/core/flags/global.flags.ts index 9ca21cff9f5..cf53c4a472f 100644 --- a/src/flags.ts +++ b/src/core/flags/global.flags.ts @@ -7,3 +7,10 @@ export const watchFlag = (description?: string) => { description: description ?? 'Enable watch mode', }); }; + +export const helpFlag = () => { + return { + help: Flags.help({ char: 'h' }) + }; +}; + diff --git a/src/core/flags/new/file.flags.ts b/src/core/flags/new/file.flags.ts new file mode 100644 index 00000000000..7c0c24ff718 --- /dev/null +++ b/src/core/flags/new/file.flags.ts @@ -0,0 +1,12 @@ +import { Flags } from '@oclif/core'; + +export const fileFlags = (exampleFlagDescription: string) => { + return { + help: Flags.help({ char: 'h' }), + 'file-name': Flags.string({ char: 'n', description: 'name of the file' }), + example: Flags.string({ char: 'e', description: exampleFlagDescription }), + studio: Flags.boolean({ char: 's', description: 'open in Studio' }), + port: Flags.integer({ char: 'p', description: 'port in which to start Studio' }), + 'no-tty': Flags.boolean({ description: 'do not use an interactive terminal' }), + }; +}; diff --git a/src/core/flags/new/glee.flags.ts b/src/core/flags/new/glee.flags.ts new file mode 100644 index 00000000000..9139b35b0fa --- /dev/null +++ b/src/core/flags/new/glee.flags.ts @@ -0,0 +1,27 @@ +import { Flags } from '@oclif/core'; + +export const gleeFlags = () => { + return { + help: Flags.help({ char: 'h' }), + name: Flags.string({ + char: 'n', + description: 'Name of the Project', + default: 'project', + }), + template: Flags.string({ + char: 't', + description: 'Name of the Template', + default: 'default', + }), + file: Flags.string({ + char: 'f', + description: + 'The path to the AsyncAPI file for generating a Glee project.', + }), + 'force-write': Flags.boolean({ + default: false, + description: + 'Force writing of the generated files to given directory even if it is a git repo with unstaged files or not empty dir (defaults to false)', + }), + }; +}; diff --git a/src/core/flags/optimize.flags.ts b/src/core/flags/optimize.flags.ts new file mode 100644 index 00000000000..b9a5aa2c707 --- /dev/null +++ b/src/core/flags/optimize.flags.ts @@ -0,0 +1,23 @@ +import { Flags } from '@oclif/core'; + +export enum Optimizations { + REMOVE_COMPONENTS='remove-components', + REUSE_COMPONENTS='reuse-components', + MOVE_DUPLICATES_TO_COMPONENTS='move-duplicates-to-components', + MOVE_ALL_TO_COMPONENTS='move-all-to-components', +} + +export enum Outputs { + TERMINAL='terminal', + NEW_FILE='new-file', + OVERWRITE='overwrite' +} + +export const optimizeFlags = () => { + return { + help: Flags.help({ char: 'h' }), + optimization: Flags.string({char: 'p', default: Object.values(Optimizations), options: Object.values(Optimizations), multiple: true, description: 'select the type of optimizations that you want to apply.'}), + output: Flags.string({char: 'o', default: Outputs.TERMINAL, options: Object.values(Outputs), description: 'select where you want the output.'}), + 'no-tty': Flags.boolean({ description: 'do not use an interactive terminal', default: false }), + }; +}; diff --git a/src/core/flags/start/studio.flags.ts b/src/core/flags/start/studio.flags.ts new file mode 100644 index 00000000000..acc4a175713 --- /dev/null +++ b/src/core/flags/start/studio.flags.ts @@ -0,0 +1,9 @@ +import { Flags } from '@oclif/core'; + +export const studioFlags = () => { + return { + help: Flags.help({ char: 'h' }), + file: Flags.string({ char: 'f', description: 'path to the AsyncAPI file to link with Studio' }), + port: Flags.integer({ char: 'p', description: 'port in which to start Studio' }), + }; +}; diff --git a/src/core/flags/validate.flags.ts b/src/core/flags/validate.flags.ts new file mode 100644 index 00000000000..b86de912be6 --- /dev/null +++ b/src/core/flags/validate.flags.ts @@ -0,0 +1,11 @@ +import { Flags } from '@oclif/core'; +import { validationFlags } from '../parser'; +import { watchFlag } from './global.flags'; + +export const validateFlags = () => { + return { + help: Flags.help({ char: 'h' }), + watch: watchFlag(), + ...validationFlags(), + }; +}; diff --git a/src/global.d.ts b/src/core/global.d.ts similarity index 100% rename from src/global.d.ts rename to src/core/global.d.ts diff --git a/src/globals.ts b/src/core/globals.ts similarity index 100% rename from src/globals.ts rename to src/core/globals.ts diff --git a/src/hooks/command_not_found/myhook.ts b/src/core/hooks/command_not_found/myhook.ts similarity index 100% rename from src/hooks/command_not_found/myhook.ts rename to src/core/hooks/command_not_found/myhook.ts diff --git a/src/models/Context.ts b/src/core/models/Context.ts similarity index 100% rename from src/models/Context.ts rename to src/core/models/Context.ts diff --git a/src/models/SpecificationFile.ts b/src/core/models/SpecificationFile.ts similarity index 99% rename from src/models/SpecificationFile.ts rename to src/core/models/SpecificationFile.ts index d81c84b1a41..30051beb80e 100644 --- a/src/models/SpecificationFile.ts +++ b/src/core/models/SpecificationFile.ts @@ -186,15 +186,15 @@ export async function fileExists(name: string): Promise { if ((await lstat(name)).isFile()) { return true; } - + const extension = name.split('.')[1]; - + const allowedExtenstion=['yml','yaml','json']; if (!allowedExtenstion.includes(extension)) { throw new ErrorLoadingSpec('invalid file',name); } - + throw new ErrorLoadingSpec('file', name); } catch (e) { throw new ErrorLoadingSpec('file', name); diff --git a/src/models/Studio.ts b/src/core/models/Studio.ts similarity index 100% rename from src/models/Studio.ts rename to src/core/models/Studio.ts diff --git a/src/parser.ts b/src/core/parser.ts similarity index 100% rename from src/parser.ts rename to src/core/parser.ts diff --git a/src/utils/generator.ts b/src/core/utils/generator.ts similarity index 100% rename from src/utils/generator.ts rename to src/core/utils/generator.ts diff --git a/test/helpers/index.ts b/test/helpers/index.ts index fdddffc68e8..20d87f46d29 100644 --- a/test/helpers/index.ts +++ b/test/helpers/index.ts @@ -1,7 +1,7 @@ import { existsSync, writeFileSync, unlinkSync,rmdirSync, mkdirSync, promises as fs } from 'fs'; import * as path from 'path'; -import { IContextFile, CONTEXT_FILE_PATH } from '../../src/models/Context'; -import SpecificationFile from '../../src/models/SpecificationFile'; +import { IContextFile, CONTEXT_FILE_PATH } from '../../src/core/models/Context'; +import SpecificationFile from '../../src/core/models/SpecificationFile'; import http from 'http'; import rimraf from 'rimraf'; diff --git a/test/integration/context.test.ts b/test/integration/context.test.ts index 0f19aadf7af..83044c53391 100644 --- a/test/integration/context.test.ts +++ b/test/integration/context.test.ts @@ -2,7 +2,7 @@ import path from 'path'; import { expect, test } from '@oclif/test'; import TestHelper from '../helpers'; -import { CONTEXT_FILE_PATH } from '../../src/models/Context'; +import { CONTEXT_FILE_PATH } from '../../src/core/models/Context'; const testHelper = new TestHelper(); @@ -119,7 +119,7 @@ describe('config:context, positive scenario', () => { done(); }); }); - + describe('config:context:init', () => { test .stderr() diff --git a/test/integration/convert.test.ts b/test/integration/convert.test.ts index 64c72ad1f9d..1f909ba3234 100644 --- a/test/integration/convert.test.ts +++ b/test/integration/convert.test.ts @@ -1,6 +1,6 @@ import path from 'path'; import { test } from '@oclif/test'; -import { NO_CONTEXTS_SAVED } from '../../src/errors/context-error'; +import { NO_CONTEXTS_SAVED } from '../../src/core/errors/context-error'; import TestHelper, { createMockServer, stopMockServer } from '../helpers'; import fs from 'fs-extra'; import { expect } from '@oclif/test'; diff --git a/test/integration/new/glee.test.ts b/test/integration/new/glee.test.ts index a7ded8abade..2745f2d7468 100644 --- a/test/integration/new/glee.test.ts +++ b/test/integration/new/glee.test.ts @@ -9,7 +9,7 @@ const successMessage = (projectName: string) => const errorMessages = { alreadyExists: (projectName: string) => - `Unable to create the project because the directory "${projectName}" already exists at "${process.cwd()}/${projectName}". + `Unable to create the project because the directory "${projectName}" already exists at "${process.cwd()}/${projectName}". To specify a different name for the new project, please run the command below with a unique project name:`}; describe('new glee', () => { @@ -27,7 +27,7 @@ describe('new glee', () => { afterEach(() => { testHelper.deleteDummyProjectDirectory(); }); - + test .stderr() .stdout() @@ -39,7 +39,7 @@ describe('new glee', () => { }); }); - describe('when new project name already exists', () => { + describe('when new project name already exists', () => { beforeEach(() => { try { testHelper.createDummyProjectDirectory(); @@ -52,7 +52,7 @@ describe('new glee', () => { afterEach(() => { testHelper.deleteDummyProjectDirectory(); - }); + }); test .stderr() diff --git a/test/integration/validate.test.ts b/test/integration/validate.test.ts index 72d5bbbdc51..81f493b35a3 100644 --- a/test/integration/validate.test.ts +++ b/test/integration/validate.test.ts @@ -2,7 +2,7 @@ import path from 'path'; import { test } from '@oclif/test'; -import { NO_CONTEXTS_SAVED } from '../../src/errors/context-error'; +import { NO_CONTEXTS_SAVED } from '../../src/core/errors/context-error'; import TestHelper, { createMockServer, stopMockServer } from '../helpers'; import { expect } from '@oclif/test';