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 committed Oct 9, 2023
1 parent bd023ef commit 4df68bc
Showing 1 changed file with 111 additions and 95 deletions.
206 changes: 111 additions & 95 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,165 +136,174 @@ class Generator {
});
}

// eslint-disable-next-line sonarjs/cognitive-complexity

/**
* Generates files from a given template and an AsyncAPIDocument object.
*
* @async
* @example
* generator
* .generate(myAsyncAPIdocument)
* .then(() => {
* console.log('Done!');
* })
* .catch(console.error);
*
* @example <caption>Using async/await</caption>
* try {
* await generator.generate(myAsyncAPIdocument);
* console.log('Done!');
* } catch (e) {
* 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}
* await generator.generate(myAsyncAPIdocument);
* console.log('Done!');
*
* @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 = {}) {
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;
if (this.output === 'fs') {
xfs.mkdirpSync(this.targetDir);
if (!this.forceWrite) await this.verifyTargetDir(this.targetDir);
} else if (this.output === 'string' && this.entrypoint === undefined) {
throw new Error('Parameter entrypoint is required when using output = "string"');
}
if (this.debug) log.setLevel('debug');
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);
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');
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 (this.output === 'fs') {
await this.generateFile(this.asyncapi, path.basename(entrypointPath), path.dirname(entrypointPath));
await this.launchHook('generate:after');
} else if (this.output === 'string') {
return this.renderFile(this.asyncapi, entrypointPath);
}
} else {
await this.generateDirectoryStructure(this.asyncapi);
await this.launchHook('generate:after');
}
} */

// Main method for generating code based on AsyncAPI document
async generate(asyncapiDocument, parseOptions = {}) {
this.validateAsyncAPIDocument(asyncapiDocument);
this.setupOutput();
this.setLogLevel();
const templateInfo = await this.installAndSetupTemplate();

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

// Validate the AsyncAPI document

/**
* 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;
}

// Setup the output based on the configured options
/**
* 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') {
this.setupFSOutput();
} else if (this.output === 'string' && this.entrypoint === undefined) {
throw new Error('Parameter entrypoint is required when using output = "string"');
}
}

// Setup file system output

/**
* 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) {
if (!this.forceWrite) {
await this.verifyTargetDir(this.targetDir);
}
}

// Set log level based on debug option

/**
* 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');
}

// Install and setup the template

/**
* 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();

return { templatePkgName, templatePkgPath };
}

// Configure the template workflow

/**
* 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');
}

// Handle the entrypoint logic

/**
* 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 (this.output === 'fs') {
await this.generateFile(this.asyncapi, path.basename(entrypointPath), path.dirname(entrypointPath));
await this.launchHook('generate:after');
Expand All @@ -306,7 +315,14 @@ class Generator {
}
}

// Execute the after-hook
/**
* 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');
}
Expand Down

0 comments on commit 4df68bc

Please sign in to comment.