Skip to content

Commit

Permalink
refactor: generate function refactored to reduce cognitive complexity (
Browse files Browse the repository at this point in the history
  • Loading branch information
swastiksuvam55 authored Oct 10, 2023
1 parent 6d42118 commit 4d347f8
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 13 deletions.
148 changes: 139 additions & 9 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class Generator {
/**
* Generates files from a given template and an AsyncAPIDocument object.
*
* @async
* @example
* await generator.generate(myAsyncAPIdocument);
* console.log('Done!');
*
* @example
* generator
* .generate(myAsyncAPIdocument)
Expand All @@ -155,48 +160,162 @@ class Generator {
* console.error(e);
* }
*
* @param {AsyncAPIDocument | string} asyncapiDocument AsyncAPIDocument object to use as source.
* @param {Object} [parseOptions={}] AsyncAPI Parser parse options. Check out {@link https://www.github.com/asyncapi/parser-js|@asyncapi/parser} for more information. Remember to use the right options to the right parser depending on the template you are using.
* @return {Promise}
* @param {AsyncAPIDocument | string} asyncapiDocument - AsyncAPIDocument object to use as source.
* @param {Object} [parseOptions={}] - AsyncAPI Parser parse options.
* Check out {@link https://www.github.com/asyncapi/parser-js|@asyncapi/parser} for more information.
* Remember to use the right options for the right parser depending on the template you are using.
* @return {Promise<void>} A Promise that resolves when the generation is completed.
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
async generate(asyncapiDocument, parseOptions = {}) {
this.validateAsyncAPIDocument(asyncapiDocument);
this.setupOutput();
this.setLogLevel();

await this.installAndSetupTemplate();
await this.configureTemplateWorkflow(parseOptions);
await this.handleEntrypoint();
await this.executeAfterHook();
}

/**
* Validates the provided AsyncAPI document.
*
* @param {*} asyncapiDocument - The AsyncAPI document to be validated.
* @throws {Error} Throws an error if the document is not valid.
* @since 10/9/2023 - 4:26:33 PM
*/
validateAsyncAPIDocument(asyncapiDocument) {
const isAlreadyParsedDocument = isAsyncAPIDocument(asyncapiDocument);
const isParsableCompatible = asyncapiDocument && typeof asyncapiDocument === 'string';

if (!isAlreadyParsedDocument && !isParsableCompatible) {
throw new Error('Parameter "asyncapiDocument" must be a non-empty string or an already parsed AsyncAPI document.');
}

this.asyncapi = this.originalAsyncAPI = asyncapiDocument;
}

/**
* Sets up the output configuration based on the specified output type.
*
* @example
* const generator = new Generator();
* generator.setupOutput();
*
* @throws {Error} If 'output' is set to 'string' without providing 'entrypoint'.
*/
setupOutput() {
if (this.output === 'fs') {
xfs.mkdirpSync(this.targetDir);
if (!this.forceWrite) await this.verifyTargetDir(this.targetDir);
this.setupFSOutput();
} else if (this.output === 'string' && this.entrypoint === undefined) {
throw new Error('Parameter entrypoint is required when using output = "string"');
}
}

/**
* Sets up the file system (FS) output configuration.
*
* This function creates the target directory if it does not exist and verifies
* the target directory if forceWrite is not enabled.
*
* @async
* @returns {Promise<void>} A promise that fulfills when the setup is complete.
*
* @throws {Error} If verification of the target directory fails and forceWrite is not enabled.
*/
async setupFSOutput() {
// Create directory if not exists
xfs.mkdirpSync(this.targetDir);

// Verify target directory if forceWrite is not enabled
if (!this.forceWrite) {
await this.verifyTargetDir(this.targetDir);
}
}

/**
* Sets the log level based on the debug option.
*
* If the debug option is enabled, the log level is set to 'debug'.
*
* @returns {void}
*/
setLogLevel() {
if (this.debug) log.setLevel('debug');
}

/**
* Installs and sets up the template for code generation.
*
* This function installs the specified template using the provided installation option,
* sets up the necessary directory paths, loads the template configuration, and returns
* information about the installed template.
*
* @async
* @returns {Promise<{ templatePkgName: string, templatePkgPath: string }>}
* A promise that resolves to an object containing the name and path of the installed template.
*/
async installAndSetupTemplate() {
const { name: templatePkgName, path: templatePkgPath } = await this.installTemplate(this.install);

this.templateDir = templatePkgPath;
this.templateName = templatePkgName;
this.templateContentDir = path.resolve(this.templateDir, TEMPLATE_CONTENT_DIRNAME);

await this.loadTemplateConfig();

await this.parseInput(this.asyncapi, parseOptions);
return { templatePkgName, templatePkgPath };
}

/**
* Configures the template workflow based on provided parsing options.
*
* This function performs the following steps:
* 1. Parses the input AsyncAPI document using the specified parse options.
* 2. Validates the template configuration and parameters.
* 3. Configures the template based on the parsed AsyncAPI document.
* 4. Registers filters, hooks, and launches the 'generate:before' hook if applicable.
*
* @async
* @param {*} parseOptions - Options for parsing the AsyncAPI document.
* @returns {Promise<void>} A promise that resolves when the configuration is completed.
*/
async configureTemplateWorkflow(parseOptions) {
// Parse input and validate template configuration
await this.parseInput(this.asyncapi, parseOptions);
validateTemplateConfig(this.templateConfig, this.templateParams, this.asyncapi);
await this.configureTemplate();

if (!isReactTemplate(this.templateConfig)) {
await registerFilters(this.nunjucks, this.templateConfig, this.templateDir, FILTERS_DIRNAME);
}

await registerHooks(this.hooks, this.templateConfig, this.templateDir, HOOKS_DIRNAME);
await this.launchHook('generate:before');
}

/**
* Handles the logic for the template entrypoint.
*
* If an entrypoint is specified:
* - Resolves the absolute path of the entrypoint file.
* - Throws an error if the entrypoint file doesn't exist.
* - Generates a file or renders content based on the output type.
* - Launches the 'generate:after' hook if the output is 'fs'.
*
* If no entrypoint is specified, generates the directory structure.
*
* @async
* @returns {Promise<void>} A promise that resolves when the entrypoint logic is completed.
*/
async handleEntrypoint() {
if (this.entrypoint) {
const entrypointPath = path.resolve(this.templateContentDir, this.entrypoint);
if (!(await exists(entrypointPath))) throw new Error(`Template entrypoint "${entrypointPath}" couldn't be found.`);

if (!(await exists(entrypointPath))) {
throw new Error(`Template entrypoint "${entrypointPath}" couldn't be found.`);
}

if (this.output === 'fs') {
await this.generateFile(this.asyncapi, path.basename(entrypointPath), path.dirname(entrypointPath));
await this.launchHook('generate:after');
Expand All @@ -205,10 +324,21 @@ class Generator {
}
} else {
await this.generateDirectoryStructure(this.asyncapi);
await this.launchHook('generate:after');
}
}

/**
* Executes the 'generate:after' hook.
*
* Launches the after-hook to perform additional actions after code generation.
*
* @async
* @returns {Promise<void>} A promise that resolves when the after-hook execution is completed.
*/
async executeAfterHook() {
await this.launchHook('generate:after');
}

/**
* Parse the generator input based on the template `templateConfig.apiVersion` value.
*/
Expand Down
11 changes: 7 additions & 4 deletions package-lock.json

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

0 comments on commit 4d347f8

Please sign in to comment.