Skip to content

Commit

Permalink
chore(design): implement design system for generate command (#1302)
Browse files Browse the repository at this point in the history
Co-authored-by: souvik <[email protected]>
  • Loading branch information
Shurtu-gal and Souvikns authored Apr 20, 2024
1 parent b6702b2 commit 6ba4b47
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 50 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ spec-examples.zip

coverage

# Analytics

.asyncapi-analytics

# Glee
assets/create-glee-app/templates/default/.glee
assets/create-glee-app/templates/tutorial/.glee
assets/create-glee-app/templates/default/docs
assets/create-glee-app/templates/tutorial/docs
assets/create-glee-app/templates/tutorial/docs
41 changes: 41 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@asyncapi/protobuf-schema-parser": "^3.2.11",
"@asyncapi/raml-dt-schema-parser": "^4.0.14",
"@asyncapi/studio": "^0.20.0",
"@clack/prompts": "^0.7.0",
"@oclif/core": "^1.26.2",
"@oclif/errors": "^1.3.6",
"@oclif/plugin-not-found": "^2.3.22",
Expand All @@ -38,6 +39,7 @@
"node-fetch": "^2.0.0",
"oclif": "^4.2.0",
"open": "^8.4.0",
"picocolors": "^1.0.0",
"reflect-metadata": "^0.1.13",
"request": "^2.88.2",
"serve-handler": "^6.1.3",
Expand Down
105 changes: 84 additions & 21 deletions src/commands/generate/fromTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Flags, CliUx } from '@oclif/core';
import { Flags } from '@oclif/core';
import Command from '../../base';
// eslint-disable-next-line
// @ts-ignore
Expand All @@ -13,11 +13,8 @@ import { ValidationError } from '../../errors/validation-error';
import { GeneratorError } from '../../errors/generator-error';
import { Parser } from '@asyncapi/parser';
import type { Example } from '@oclif/core/lib/interfaces';

const red = (text: string) => `\x1b[31m${text}\x1b[0m`;
const magenta = (text: string) => `\x1b[35m${text}\x1b[0m`;
const yellow = (text: string) => `\x1b[33m${text}\x1b[0m`;
const green = (text: string) => `\x1b[32m${text}\x1b[0m`;
import { intro, isCancel, spinner, text } from '@clack/prompts';
import { inverse, yellow, magenta, green, red } from 'picocolors';

interface IMapBaseUrlToFlag {
url: string,
Expand Down Expand Up @@ -68,6 +65,11 @@ export default class Template extends Command {
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,
Expand Down Expand Up @@ -103,18 +105,27 @@ export default class Template extends Command {
};

static args = [
{ name: 'asyncapi', description: '- Local path, url or context-name pointing to AsyncAPI file', required: true },
{ name: 'template', description: '- Name of the generator template like for example @asyncapi/html-template or https://github.com/asyncapi/html-template', required: true }
{ name: 'asyncapi', description: '- Local path, url or context-name pointing to AsyncAPI file' },
{ name: 'template', description: '- Name of the generator template like for example @asyncapi/html-template or https://github.com/asyncapi/html-template' },
];

parser = new Parser();

async run() {
const { args, flags } = await this.parse(Template); // NOSONAR
const interactive = !flags['no-interactive'];

let { asyncapi, template } = args;
let output = flags.output as string;
if (interactive) {
intro(inverse('AsyncAPI Generator'));

const parsedArgs = await this.parseArgs(args, output);
asyncapi = parsedArgs.asyncapi;
template = parsedArgs.template;
output = parsedArgs.output;
}

const asyncapi = args['asyncapi'];
const template = args['template'];
const output = flags.output || process.cwd();
const parsedFlags = this.parseFlags(flags['disable-hook'], flags['param'], flags['map-base-url']);
const options = {
forceWrite: flags['force-write'],
Expand Down Expand Up @@ -142,13 +153,66 @@ export default class Template extends Command {
this.error(`${template} template does not support AsyncAPI v3 documents, please checkout ${v3IssueLink}`);
}
}
await this.generate(asyncapi, template, output, options, genOption);
await this.generate(asyncapi, template, output, options, genOption, interactive);
if (watchTemplate) {
const watcherHandler = this.watcherHandler(asyncapi, template, output, options, genOption);
const watcherHandler = this.watcherHandler(asyncapi, template, output, options, genOption, interactive);
await this.runWatchMode(asyncapi, template, output, watcherHandler);
}
}

private async parseArgs(args: Record<string, any>, output?: string): Promise<{ asyncapi: string; template: string; output: string; }> {
let asyncapi = args['asyncapi'];
let template = args['template'];
const cancellationMessage = 'Operation cancelled';

if (!asyncapi) {
asyncapi = await text({
message: 'Please provide the path to the AsyncAPI document',
placeholder: 'asyncapi.yaml',
defaultValue: 'asyncapi.yaml',
validate(value: string) {
if (!value) {
return 'The path to the AsyncAPI document is required';
} else if (!fs.existsSync(value)) {
return 'The file does not exist';
}
}
});
}

if (isCancel(asyncapi)) {
this.error(cancellationMessage, { exit: 1 });
}

if (!template) {
template = await text({
message: 'Please provide the name of the generator template',
placeholder: '@asyncapi/html-template',
defaultValue: '@asyncapi/html-template',
});
}

if (!output) {
output = await text({
message: 'Please provide the output directory',
placeholder: './docs',
validate(value: string) {
if (!value) {
return 'The output directory is required';
} else if (typeof value !== 'string') {
return 'The output directory must be a string';
}
}
}) as string;
}

if (isCancel(output) || isCancel(template)) {
this.error(cancellationMessage, { exit: 1 });
}

return { asyncapi, template, output };
}

private parseFlags(disableHooks?: string[], params?: string[], mapBaseUrl?: string): ParsedFlags {
return {
params: this.paramParser(params),
Expand Down Expand Up @@ -204,7 +268,7 @@ export default class Template extends Command {
return mapBaseURLToFolder;
}

private async generate(asyncapi: string | undefined, template: string, output: string, options: any, genOption: any) {
private async generate(asyncapi: string | undefined, template: string, output: string, options: any, genOption: any, interactive = true) {
let specification: Specification;
try {
specification = await load(asyncapi);
Expand All @@ -218,16 +282,15 @@ export default class Template extends Command {
);
}
const generator = new AsyncAPIGenerator(template, output || path.resolve(os.tmpdir(), 'asyncapi-generator'), options);

CliUx.ux.action.start('Generation in progress. Keep calm and wait a bit');
const s = interactive ? spinner() : { start: () => null, stop: (string: string) => console.log(string) };
s.start('Generation in progress. Keep calm and wait a bit');
try {
await generator.generateFromString(specification.text(), genOption);
CliUx.ux.action.stop();
} catch (err: any) {
CliUx.ux.action.stop('done\n');
s.stop('Generation failed');
throw new GeneratorError(err);
}
console.log(`${yellow('Check out your shiny new generated files at ') + magenta(output) + yellow('.')}\n`);
s.stop(`${yellow('Check out your shiny new generated files at ') + magenta(output) + yellow('.')}\n`);
}

private async runWatchMode(asyncapi: string | undefined, template: string, output: string, watchHandler: ReturnType<typeof this.watcherHandler>) {
Expand Down Expand Up @@ -270,7 +333,7 @@ export default class Template extends Command {
});
}

private watcherHandler(asyncapi: string, template: string, output: string, options: Record<string, any>, genOption: any): (changedFiles: Record<string, any>) => Promise<void> {
private watcherHandler(asyncapi: string, template: string, output: string, options: Record<string, any>, genOption: any, interactive: boolean): (changedFiles: Record<string, any>) => Promise<void> {
return async (changedFiles: Record<string, any>): Promise<void> => {
console.clear();
console.log('[WATCHER] Change detected');
Expand All @@ -292,7 +355,7 @@ export default class Template extends Command {
this.log(`\t${magenta(value.path)} was ${eventText}`);
}
try {
await this.generate(asyncapi, template, output, options, genOption);
await this.generate(asyncapi, template, output, options, genOption, interactive);
} catch (err: any) {
throw new GeneratorError(err);
}
Expand Down
Loading

0 comments on commit 6ba4b47

Please sign in to comment.