From 01b92b081965d16bf0a6274e874f5baa0dda57c2 Mon Sep 17 00:00:00 2001 From: Bedi Gupta Date: Sat, 5 Oct 2024 15:52:34 +0530 Subject: [PATCH] using an optional chain expression Provide a detailed explanation in the file comment and remove unnecessary changes made by my code formatter. --- apps/generator/lib/generator.js | 700 +++++++++----------------------- 1 file changed, 202 insertions(+), 498 deletions(-) diff --git a/apps/generator/lib/generator.js b/apps/generator/lib/generator.js index b89ddd82d..7ad64dabe 100644 --- a/apps/generator/lib/generator.js +++ b/apps/generator/lib/generator.js @@ -1,25 +1,21 @@ -const path = require("path"); -const fs = require("fs"); -const xfs = require("fs.extra"); -const minimatch = require("minimatch"); -const jmespath = require("jmespath"); -const filenamify = require("filenamify"); -const git = require("simple-git"); -const log = require("loglevel"); -const Arborist = require("@npmcli/arborist"); -const Config = require("@npmcli/config"); -const requireg = require("requireg"); -const npmPath = requireg.resolve("npm").replace("index.js", ""); - -const { isAsyncAPIDocument } = require("@asyncapi/parser/cjs/document"); - -const { - configureReact, - renderReact, - saveRenderedReactContent, -} = require("./renderer/react"); -const { configureNunjucks, renderNunjucks } = require("./renderer/nunjucks"); -const { validateTemplateConfig } = require("./templateConfigValidator"); +const path = require('path'); +const fs = require('fs'); +const xfs = require('fs.extra'); +const minimatch = require('minimatch'); +const jmespath = require('jmespath'); +const filenamify = require('filenamify'); +const git = require('simple-git'); +const log = require('loglevel'); +const Arborist = require('@npmcli/arborist'); +const Config = require('@npmcli/config'); +const requireg = require('requireg'); +const npmPath = requireg.resolve('npm').replace('index.js',''); + +const { isAsyncAPIDocument } = require('@asyncapi/parser/cjs/document'); + +const { configureReact, renderReact, saveRenderedReactContent } = require('./renderer/react'); +const { configureNunjucks, renderNunjucks } = require('./renderer/nunjucks'); +const { validateTemplateConfig } = require('./templateConfigValidator'); const { convertMapToObject, isFileSystemPath, @@ -33,49 +29,32 @@ const { isJsFile, getTemplateDetails, convertCollectionToObject, -} = require("./utils"); -const { parse, usesNewAPI, getProperApiDocument } = require("./parser"); -const { registerFilters } = require("./filtersRegistry"); -const { registerHooks } = require("./hooksRegistry"); -const { - definitions, - flatten, - shorthands, -} = require("@npmcli/config/lib/definitions"); - -const FILTERS_DIRNAME = "filters"; -const HOOKS_DIRNAME = "hooks"; -const CONFIG_FILENAME = "package.json"; -const PACKAGE_JSON_FILENAME = "package.json"; -const GIT_IGNORE_FILENAME = "{.gitignore}"; -const NPM_IGNORE_FILENAME = "{.npmignore}"; -const ROOT_DIR = path.resolve(__dirname, ".."); -const DEFAULT_TEMPLATES_DIR = path.resolve(ROOT_DIR, "node_modules"); - -const TRANSPILED_TEMPLATE_LOCATION = "__transpiled"; -const TEMPLATE_CONTENT_DIRNAME = "template"; -const GENERATOR_OPTIONS = [ - "debug", - "disabledHooks", - "entrypoint", - "forceWrite", - "install", - "noOverwriteGlobs", - "output", - "templateParams", - "mapBaseUrlToFolder", - "url", - "auth", - "token", - "registry", - "compile", -]; -const logMessage = require("./logMessages"); - -const shouldIgnoreFile = (filePath) => filePath.startsWith(`.git${path.sep}`); - -const shouldIgnoreDir = (dirPath) => - dirPath === ".git" || dirPath.startsWith(`.git${path.sep}`); +} = require('./utils'); +const { parse, usesNewAPI, getProperApiDocument } = require('./parser'); +const { registerFilters } = require('./filtersRegistry'); +const { registerHooks } = require('./hooksRegistry'); +const { definitions, flatten, shorthands } = require('@npmcli/config/lib/definitions'); + +const FILTERS_DIRNAME = 'filters'; +const HOOKS_DIRNAME = 'hooks'; +const CONFIG_FILENAME = 'package.json'; +const PACKAGE_JSON_FILENAME = 'package.json'; +const GIT_IGNORE_FILENAME = '{.gitignore}'; +const NPM_IGNORE_FILENAME = '{.npmignore}'; +const ROOT_DIR = path.resolve(__dirname, '..'); +const DEFAULT_TEMPLATES_DIR = path.resolve(ROOT_DIR, 'node_modules'); + +const TRANSPILED_TEMPLATE_LOCATION = '__transpiled'; +const TEMPLATE_CONTENT_DIRNAME = 'template'; +const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder', 'url', 'auth', 'token', 'registry', 'compile']; +const logMessage = require('./logMessages'); + +const shouldIgnoreFile = filePath => + filePath.startsWith(`.git${path.sep}`); + +const shouldIgnoreDir = dirPath => + dirPath === '.git' + || dirPath.startsWith(`.git${path.sep}`); class Generator { /** @@ -112,32 +91,12 @@ class Generator { * @param {String} [options.registry.token] Optional parameter to pass npm registry auth token that you can grab from .npmrc file */ - constructor( - templateName, - targetDir, - { - templateParams = {}, - entrypoint, - noOverwriteGlobs, - disabledHooks, - output = "fs", - forceWrite = false, - install = false, - debug = false, - mapBaseUrlToFolder = {}, - registry = {}, - compile = true, - } = {} - ) { + constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {}, registry = {}, compile = true } = {}) { const options = arguments[arguments.length - 1]; this.verifyoptions(options); - if (!templateName) throw new Error("No template name has been specified."); - if (!entrypoint && !targetDir) - throw new Error("No target directory has been specified."); - if (!["fs", "string"].includes(output)) - throw new Error( - `Invalid output type ${output}. Valid values are 'fs' and 'string'.` - ); + if (!templateName) throw new Error('No template name has been specified.'); + if (!entrypoint && !targetDir) throw new Error('No target directory has been specified.'); + if (!['fs', 'string'].includes(output)) throw new Error(`Invalid output type ${output}. Valid values are 'fs' and 'string'.`); /** @type {Boolean} Whether to compile the template or use the cached transpiled version provided by template in '__transpiled' folder. */ this.compile = compile; /** @type {Object} Npm registry information. */ @@ -170,8 +129,7 @@ class Generator { // Load template configuration /** @type {Object} The template parameters. The structure for this object is based on each individual template. */ this.templateParams = {}; - // Made this part easier to read and less error-prone - Object.keys(templateParams).forEach((key) => { + Object.keys(templateParams).forEach(key => { Object.defineProperty(this.templateParams, key, { enumerable: true, get: () => { @@ -201,25 +159,19 @@ class Generator { } /** - * Check if the Registry Options are valid or not. - * - * @private - * @param {Object} invalidRegOptions Invalid Registry Options. - * - */ + * Check if the Registry Options are valid or not. + * + * @private + * @param {Object} invalidRegOptions Invalid Registry Options. + * + */ verifyoptions(Options) { - if (typeof Options !== "object") return []; - const invalidOptions = Object.keys(Options).filter( - (param) => !GENERATOR_OPTIONS.includes(param) - ); + if (typeof Options !== 'object') return []; + const invalidOptions = Object.keys(Options).filter(param => !GENERATOR_OPTIONS.includes(param)); if (invalidOptions.length > 0) { - throw new Error( - `These options are not supported by the generator: ${invalidOptions.join( - ", " - )}` - ); + throw new Error(`These options are not supported by the generator: ${invalidOptions.join(', ')}`); } } @@ -273,13 +225,10 @@ class Generator { */ validateAsyncAPIDocument(asyncapiDocument) { const isAlreadyParsedDocument = isAsyncAPIDocument(asyncapiDocument); - const isParsableCompatible = - asyncapiDocument && typeof asyncapiDocument === "string"; + 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.' - ); + throw new Error('Parameter "asyncapiDocument" must be a non-empty string or an already parsed AsyncAPI document.'); } this.asyncapi = this.originalAsyncAPI = asyncapiDocument; @@ -297,12 +246,10 @@ class Generator { * @throws {Error} If 'output' is set to 'string' without providing 'entrypoint'. */ async setupOutput() { - if (this.output === "fs") { + if (this.output === 'fs') { await this.setupFSOutput(); - } else if (this.output === "string" && this.entrypoint === undefined) { - throw new Error( - 'Parameter entrypoint is required when using output = "string"' - ); + } else if (this.output === 'string' && this.entrypoint === undefined) { + throw new Error('Parameter entrypoint is required when using output = "string"'); } } @@ -335,7 +282,7 @@ class Generator { * @returns {void} */ setLogLevel() { - if (this.debug) log.setLevel("debug"); + if (this.debug) log.setLevel('debug'); } /** @@ -350,14 +297,10 @@ class Generator { * 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); + 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 - ); + this.templateContentDir = path.resolve(this.templateDir, TEMPLATE_CONTENT_DIRNAME); await this.loadTemplateConfig(); @@ -380,27 +323,13 @@ class Generator { async configureTemplateWorkflow(parseOptions) { // Parse input and validate template configuration await this.parseInput(this.asyncapi, parseOptions); - validateTemplateConfig( - this.templateConfig, - this.templateParams, - this.asyncapi - ); + 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 registerFilters(this.nunjucks, this.templateConfig, this.templateDir, FILTERS_DIRNAME); } - await registerHooks( - this.hooks, - this.templateConfig, - this.templateDir, - HOOKS_DIRNAME - ); - await this.launchHook("generate:before"); + await registerHooks(this.hooks, this.templateConfig, this.templateDir, HOOKS_DIRNAME); + await this.launchHook('generate:before'); } /** @@ -419,23 +348,14 @@ class Generator { */ async handleEntrypoint() { if (this.entrypoint) { - const entrypointPath = path.resolve( - this.templateContentDir, - this.entrypoint - ); + const entrypointPath = path.resolve(this.templateContentDir, this.entrypoint); if (!(await exists(entrypointPath))) { - throw new Error( - `Template entrypoint "${entrypointPath}" couldn't be found.` - ); + 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") { + 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 await this.renderFile(this.asyncapi, entrypointPath); } } else { @@ -452,7 +372,7 @@ class Generator { * @returns {Promise} A promise that resolves when the after-hook execution is completed. */ async executeAfterHook() { - await this.launchHook("generate:after"); + await this.launchHook('generate:after'); } /** @@ -462,33 +382,18 @@ class Generator { const isAlreadyParsedDocument = isAsyncAPIDocument(asyncapiDocument); // use the expected document API based on `templateConfig.apiVersion` value if (isAlreadyParsedDocument) { - this.asyncapi = getProperApiDocument( - asyncapiDocument, - this.templateConfig - ); + this.asyncapi = getProperApiDocument(asyncapiDocument, this.templateConfig); } else { /** @type {AsyncAPIDocument} Parsed AsyncAPI schema. See {@link https://github.com/asyncapi/parser-js/blob/master/API.md#module_@asyncapi/parser+AsyncAPIDocument|AsyncAPIDocument} for details on object structure. */ - const { document, diagnostics } = await parse( - asyncapiDocument, - parseOptions, - this - ); + const { document, diagnostics } = await parse(asyncapiDocument, parseOptions, this); if (!document) { - const err = new Error( - "Input is not a correct AsyncAPI document so it cannot be processed." - ); + const err = new Error('Input is not a correct AsyncAPI document so it cannot be processed.'); err.diagnostics = diagnostics; for (const diag of diagnostics) { console.error( - `Diagnostic err: ${diag["message"]} in path ${JSON.stringify( - diag["path"] - )} starting ` + - `L${diag["range"]["start"]["line"] + 1} C${ - diag["range"]["start"]["character"] - }, ending ` + - `L${diag["range"]["end"]["line"] + 1} C${ - diag["range"]["end"]["character"] - }` + `Diagnostic err: ${diag['message']} in path ${JSON.stringify(diag['path'])} starting `+ + `L${diag['range']['start']['line'] + 1} C${diag['range']['start']['character']}, ending `+ + `L${diag['range']['end']['line'] + 1} C${diag['range']['end']['character']}` ); } throw err; @@ -503,11 +408,7 @@ class Generator { */ async configureTemplate() { if (isReactTemplate(this.templateConfig) && this.compile) { - await configureReact( - this.templateDir, - this.templateContentDir, - TRANSPILED_TEMPLATE_LOCATION - ); + await configureReact(this.templateDir, this.templateContentDir, TRANSPILED_TEMPLATE_LOCATION); } else { this.nunjucks = configureNunjucks(this.debug, this.templateDir); } @@ -553,8 +454,7 @@ class Generator { * @return {Promise} */ async generateFromString(asyncapiString, parseOptions = {}) { - const isParsableCompatible = - asyncapiString && typeof asyncapiString === "string"; + const isParsableCompatible = asyncapiString && typeof asyncapiString === 'string'; if (!isParsableCompatible) { throw new Error('Parameter "asyncapiString" must be a non-empty string.'); } @@ -611,7 +511,7 @@ class Generator { * @return {Promise} */ async generateFromFile(asyncapiFile) { - const doc = await readFile(asyncapiFile, { encoding: "utf8" }); + const doc = await readFile(asyncapiFile, { encoding: 'utf8' }); return await this.generate(doc, { path: asyncapiFile }); } @@ -632,15 +532,8 @@ class Generator { * @param {String} [templatesDir=DEFAULT_TEMPLATES_DIR] Path to the directory where the templates are installed. * @return {Promise} */ - static async getTemplateFile( - templateName, - filePath, - templatesDir = DEFAULT_TEMPLATES_DIR - ) { - return await readFile( - path.resolve(templatesDir, templateName, filePath), - "utf8" - ); + static async getTemplateFile(templateName, filePath, templatesDir = DEFAULT_TEMPLATES_DIR) { + return await readFile(path.resolve(templatesDir, templateName, filePath), 'utf8'); } /** @@ -648,8 +541,8 @@ class Generator { * @param {Object} arbOptions ArbOptions to intialise the Registry details. */ initialiseArbOptions(arbOptions) { - let registryUrl = "registry.npmjs.org"; - let authorizationName = "anonymous"; + let registryUrl = 'registry.npmjs.org'; + let authorizationName = 'anonymous'; const providedRegistry = this.registry.url; if (providedRegistry) { @@ -657,7 +550,7 @@ class Generator { registryUrl = providedRegistry; } - const domainName = registryUrl.replace(/^https?:\/\//, ""); + const domainName = registryUrl.replace(/^https?:\/\//, ''); //doing basic if/else so basically only one auth type is used and token as more secure is primary if (this.registry.token) { authorizationName = `//${domainName}:_authToken`; @@ -668,9 +561,7 @@ class Generator { } //not sharing in logs neither token nor auth for security reasons - log.debug( - `Using npm registry ${registryUrl} and authorization type ${authorizationName} to handle template installation.` - ); + log.debug(`Using npm registry ${registryUrl} and authorization type ${authorizationName} to handle template installation.`); } /** @@ -685,19 +576,15 @@ class Generator { let packageVersion; try { - installedPkg = getTemplateDetails( - this.templateName, - PACKAGE_JSON_FILENAME - ); + installedPkg = getTemplateDetails(this.templateName, PACKAGE_JSON_FILENAME); pkgPath = installedPkg && installedPkg.pkgPath; packageVersion = installedPkg && installedPkg.version; log.debug(logMessage.templateSource(pkgPath)); - if (packageVersion) - log.debug(logMessage.templateVersion(packageVersion)); + if (packageVersion) log.debug(logMessage.templateVersion(packageVersion)); return { name: installedPkg.name, - path: pkgPath, + path: pkgPath }; } catch (e) { log.debug(logMessage.packageNotAvailable(installedPkg), e); @@ -705,24 +592,21 @@ class Generator { } } - const debugMessage = force - ? logMessage.TEMPLATE_INSTALL_FLAG_MSG - : logMessage.TEMPLATE_INSTALL_DISK_MSG; + const debugMessage = force ? logMessage.TEMPLATE_INSTALL_FLAG_MSG : logMessage.TEMPLATE_INSTALL_DISK_MSG; log.debug(logMessage.installationDebugMessage(debugMessage)); - if (isFileSystemPath(this.templateName)) - log.debug(logMessage.NPM_INSTALL_TRIGGER); + if (isFileSystemPath(this.templateName)) log.debug(logMessage.NPM_INSTALL_TRIGGER); const config = new Config({ definitions, flatten, shorthands, - npmPath, + npmPath }); await config.load(); - const arbOptions = { ...{ path: ROOT_DIR }, ...config.flat }; + const arbOptions = {...{path: ROOT_DIR}, ...config.flat}; if (Object.keys(this.registry).length !== 0) { this.initialiseArbOptions(arbOptions); @@ -732,24 +616,18 @@ class Generator { try { const installResult = await arb.reify({ add: [this.templateName], - saveType: "prod", - save: false, + saveType: 'prod', + save: false }); - const addResult = arb[Symbol.for("resolvedAdd")]; - if (!addResult) - throw new Error( - "Unable to resolve the name of the added package. It was most probably not added to node_modules successfully" - ); + const addResult = arb[Symbol.for('resolvedAdd')]; + if (!addResult) throw new Error('Unable to resolve the name of the added package. It was most probably not added to node_modules successfully'); const packageName = addResult[0].name; const packageVersion = installResult.children.get(packageName).version; const packagePath = installResult.children.get(packageName).path; - if (!isFileSystemPath(this.templateName)) - log.debug( - logMessage.templateSuccessfullyInstalled(packageName, packagePath) - ); + if (!isFileSystemPath(this.templateName)) log.debug(logMessage.templateSuccessfullyInstalled(packageName, packagePath)); if (packageVersion) log.debug(logMessage.templateVersion(packageVersion)); return { @@ -757,7 +635,7 @@ class Generator { path: packagePath, }; } catch (err) { - throw new Error("Installation failed", err); + throw new Error('Installation failed', err); } } @@ -770,28 +648,18 @@ class Generator { getAllParameters(asyncapiDocument) { const parameters = new Map(); if (usesNewAPI(this.templateConfig)) { - asyncapiDocument - .channels() - .all() - .forEach((channel) => { - channel - .parameters() - .all() - .forEach((parameter) => { - parameters.set(parameter.id(), parameter); - }); - }); - - asyncapiDocument - .components() - .channelParameters() - .all() - .forEach((parameter) => { + asyncapiDocument.channels().all().forEach(channel => { + channel.parameters().all().forEach(parameter => { parameters.set(parameter.id(), parameter); }); + }); + + asyncapiDocument.components().channelParameters().all().forEach(parameter => { + parameters.set(parameter.id(), parameter); + }); } else { if (asyncapiDocument.hasChannels()) { - asyncapiDocument.channelNames().forEach((channelName) => { + asyncapiDocument.channelNames().forEach(channelName => { const channel = asyncapiDocument.channel(channelName); for (const [key, value] of Object.entries(channel.parameters())) { parameters.set(key, value); @@ -800,9 +668,7 @@ class Generator { } if (asyncapiDocument.hasComponents()) { - for (const [key, value] of Object.entries( - asyncapiDocument.components().parameters() - )) { + for (const [key, value] of Object.entries(asyncapiDocument.components().parameters())) { parameters.set(key, value); } } @@ -823,23 +689,18 @@ class Generator { xfs.mkdirpSync(this.targetDir); const walker = xfs.walk(this.templateContentDir, { - followLinks: false, + followLinks: false }); - walker.on("file", async (root, stats, next) => { + walker.on('file', async (root, stats, next) => { try { - await this.filesGenerationHandler( - asyncapiDocument, - root, - stats, - next - ); + await this.filesGenerationHandler(asyncapiDocument, root, stats, next); } catch (e) { reject(e); } }); - walker.on("directory", async (root, stats, next) => { + walker.on('directory', async (root, stats, next) => { try { this.ignoredDirHandler(root, stats, next); } catch (e) { @@ -847,11 +708,11 @@ class Generator { } }); - walker.on("errors", (root, nodeStatsArray) => { + walker.on('errors', (root, nodeStatsArray) => { reject(nodeStatsArray); }); - walker.on("end", async () => { + walker.on('end', async () => { resolve(); }); }); @@ -866,10 +727,7 @@ class Generator { * @param {Function} next Callback function */ ignoredDirHandler(root, stats, next) { - const relativeDir = path.relative( - this.templateContentDir, - path.resolve(root, stats.name) - ); + const relativeDir = path.relative(this.templateContentDir, path.resolve(root, stats.name)); const dirPath = path.resolve(this.targetDir, relativeDir); if (!shouldIgnoreDir(relativeDir)) { xfs.mkdirpSync(dirPath); @@ -891,49 +749,22 @@ class Generator { let fileNamesForSeparation = {}; if (usesNewAPI(this.templateConfig)) { fileNamesForSeparation = { - channel: convertCollectionToObject( - asyncapiDocument.channels().all(), - "address" - ), - message: convertCollectionToObject( - asyncapiDocument.messages().all(), - "id" - ), - securityScheme: convertCollectionToObject( - asyncapiDocument.components().securitySchemes().all(), - "id" - ), - schema: convertCollectionToObject( - asyncapiDocument.components().schemas().all(), - "id" - ), - objectSchema: convertCollectionToObject( - asyncapiDocument - .schemas() - .all() - .filter((schema) => schema.type() === "object"), - "id" - ), + channel: convertCollectionToObject(asyncapiDocument.channels().all(), 'address'), + message: convertCollectionToObject(asyncapiDocument.messages().all(), 'id'), + securityScheme: convertCollectionToObject(asyncapiDocument.components().securitySchemes().all(), 'id'), + schema: convertCollectionToObject(asyncapiDocument.components().schemas().all(), 'id'), + objectSchema: convertCollectionToObject(asyncapiDocument.schemas().all().filter(schema => schema.type() === 'object'), 'id'), parameter: convertMapToObject(this.getAllParameters(asyncapiDocument)), - everySchema: convertCollectionToObject( - asyncapiDocument.schemas().all(), - "id" - ), + everySchema: convertCollectionToObject(asyncapiDocument.schemas().all(), 'id'), }; } else { const objectSchema = {}; - asyncapiDocument.allSchemas().forEach((schema, schemaId) => { - if (schema.type() === "object") objectSchema[schemaId] = schema; - }); + asyncapiDocument.allSchemas().forEach((schema, schemaId) => { if (schema.type() === 'object') objectSchema[schemaId] = schema; }); fileNamesForSeparation = { channel: asyncapiDocument.channels(), message: convertMapToObject(asyncapiDocument.allMessages()), - securityScheme: asyncapiDocument.components() - ? asyncapiDocument.components().securitySchemes() - : {}, - schema: asyncapiDocument.components() - ? asyncapiDocument.components().schemas() - : {}, + securityScheme: asyncapiDocument.components() ? asyncapiDocument.components().securitySchemes() : {}, + schema: asyncapiDocument.components() ? asyncapiDocument.components().schemas() : {}, objectSchema, parameter: convertMapToObject(this.getAllParameters(asyncapiDocument)), everySchema: convertMapToObject(asyncapiDocument.allSchemas()), @@ -943,28 +774,15 @@ class Generator { // Check if the filename dictates it should be separated let wasSeparated = false; for (const prop in fileNamesForSeparation) { - if ( - Object.hasOwn(fileNamesForSeparation, prop) && // Use Object.hasOwn() here - stats.name.includes(`$$${prop}$$`) - ) { - await this.generateSeparateFiles( - asyncapiDocument, - fileNamesForSeparation[prop], - prop, - stats.name, - root - ); - const templateFilePath = path.relative( - this.templateContentDir, - path.resolve(root, stats.name) - ); + if (Object.prototype.hasOwnProperty.call(fileNamesForSeparation, prop) && stats.name.includes(`$$${prop}$$`)) { + await this.generateSeparateFiles(asyncapiDocument, fileNamesForSeparation[prop], prop, stats.name, root); + const templateFilePath = path.relative(this.templateContentDir, path.resolve(root, stats.name)); fs.unlink(path.resolve(this.targetDir, templateFilePath), next); wasSeparated = true; - // The filename can only contain 1 specifier (message, scheme, etc.) + //The filename can only contain 1 specifier (message, scheme etc) break; } } - // If it was not separated process normally if (!wasSeparated) { await this.generateFile(asyncapiDocument, stats.name, root); @@ -983,27 +801,12 @@ class Generator { * @param {String} baseDir Base directory of the given file name. * @returns {Promise} */ - async generateSeparateFiles( - asyncapiDocument, - array, - template, - fileName, - baseDir - ) { + async generateSeparateFiles(asyncapiDocument, array, template, fileName, baseDir) { const promises = []; Object.keys(array).forEach((name) => { const component = array[name]; - promises.push( - this.generateSeparateFile( - asyncapiDocument, - name, - component, - template, - fileName, - baseDir - ) - ); + promises.push(this.generateSeparateFile(asyncapiDocument, name, component, template, fileName, baseDir)); }); return Promise.all(promises); @@ -1021,54 +824,30 @@ class Generator { * @param {String} baseDir Base directory of the given file name. * @returns {Promise} */ - async generateSeparateFile( - asyncapiDocument, - name, - component, - template, - fileName, - baseDir - ) { + async generateSeparateFile(asyncapiDocument, name, component, template, fileName, baseDir) { const relativeBaseDir = path.relative(this.templateContentDir, baseDir); - const setFileTemplateNameHookName = "setFileTemplateName"; + const setFileTemplateNameHookName = 'setFileTemplateName'; let filename = name; if (this.isHookAvailable(setFileTemplateNameHookName)) { - const filenamesFromHooks = await this.launchHook( - setFileTemplateNameHookName, - { originalFilename: filename } - ); + const filenamesFromHooks = await this.launchHook(setFileTemplateNameHookName, { originalFilename: filename }); //Use the result of the first hook filename = filenamesFromHooks[0]; } else { - filename = filenamify(filename, { replacement: "-", maxLength: 255 }); + filename = filenamify(filename, { replacement: '-', maxLength: 255 }); } const newFileName = fileName.replace(`\$\$${template}\$\$`, filename); - const targetFile = path.resolve( - this.targetDir, - relativeBaseDir, - newFileName - ); + const targetFile = path.resolve(this.targetDir, relativeBaseDir, newFileName); const relativeTargetFile = path.relative(this.targetDir, targetFile); - const shouldOverwriteFile = await this.shouldOverwriteFile( - relativeTargetFile - ); + const shouldOverwriteFile = await this.shouldOverwriteFile(relativeTargetFile); if (!shouldOverwriteFile) return; //Ensure the same object are parsed to the renderFile method as before. const temp = {}; - const key = - template === "everySchema" || template === "objectSchema" - ? "schema" - : template; + const key = template === 'everySchema' || template === 'objectSchema' ? 'schema' : template; temp[`${key}Name`] = name; temp[key] = component; - await this.renderAndWriteToFile( - asyncapiDocument, - path.resolve(baseDir, fileName), - targetFile, - temp - ); + await this.renderAndWriteToFile(asyncapiDocument, path.resolve(baseDir, fileName), targetFile, temp); } /** @@ -1080,25 +859,12 @@ class Generator { * @param {String} outputPath Path to the resulting rendered file. * @param {Object} [extraTemplateData] Extra data to pass to the template. */ - async renderAndWriteToFile( - asyncapiDocument, - templateFilePath, - outputpath, - extraTemplateData - ) { - const renderContent = await this.renderFile( - asyncapiDocument, - templateFilePath, - extraTemplateData - ); + async renderAndWriteToFile(asyncapiDocument, templateFilePath, outputpath, extraTemplateData) { + const renderContent = await this.renderFile(asyncapiDocument, templateFilePath, extraTemplateData); if (renderContent === undefined) { return; } else if (isReactTemplate(this.templateConfig)) { - await saveRenderedReactContent( - renderContent, - outputpath, - this.noOverwriteGlobs - ); + await saveRenderedReactContent(renderContent, outputpath, this.noOverwriteGlobs); } else { await writeFile(outputpath, renderContent); } @@ -1115,59 +881,34 @@ class Generator { */ async generateFile(asyncapiDocument, fileName, baseDir) { const sourceFile = path.resolve(baseDir, fileName); - const relativeSourceFile = path.relative( - this.templateContentDir, - sourceFile - ); - const targetFile = path.resolve( - this.targetDir, - this.maybeRenameSourceFile(relativeSourceFile) - ); + const relativeSourceFile = path.relative(this.templateContentDir, sourceFile); + const targetFile = path.resolve(this.targetDir, this.maybeRenameSourceFile(relativeSourceFile)); const relativeTargetFile = path.relative(this.targetDir, targetFile); if (shouldIgnoreFile(relativeSourceFile)) return; - const shouldOverwriteFile = await this.shouldOverwriteFile( - relativeTargetFile - ); + const shouldOverwriteFile = await this.shouldOverwriteFile(relativeTargetFile); if (!shouldOverwriteFile) return; - // Refactored the conditional files check to use optional chaining and simplify logic. - if (this.templateConfig.conditionalFiles?.[relativeSourceFile]) { - const server = - this.templateParams.server && - asyncapiDocument.servers().get(this.templateParams.server); - const source = jmespath.search( - { - ...asyncapiDocument.json(), - ...{ - server: server ? server.json() : undefined, - }, + if (this.templateConfig.conditionalFiles && this.templateConfig.conditionalFiles[relativeSourceFile]) { + const server = this.templateParams.server && asyncapiDocument.servers().get(this.templateParams.server); + const source = jmespath.search({ + ...asyncapiDocument.json(), + ...{ + server: server ? server.json() : undefined, }, - this.templateConfig.conditionalFiles[relativeSourceFile].subject - ); + }, this.templateConfig.conditionalFiles[relativeSourceFile].subject); - if (!source) - return log.debug( - logMessage.relativeSourceFileNotGenerated( - relativeSourceFile, - this.templateConfig.conditionalFiles[relativeSourceFile].subject - ) - ); + if (!source) return log.debug(logMessage.relativeSourceFileNotGenerated(relativeSourceFile, this.templateConfig.conditionalFiles[relativeSourceFile].subject)); if (source) { - const validate = - this.templateConfig.conditionalFiles[relativeSourceFile].validate; + const validate = this.templateConfig.conditionalFiles[relativeSourceFile].validate; const valid = validate(source); - if (!valid) - return log.debug( - logMessage.conditionalFilesMatched(relativeSourceFile) - ); + if (!valid) return log.debug(logMessage.conditionalFilesMatched(relativeSourceFile)); } } - /**This refactored version uses a single return statement, which makes the code more concise and easier to read. */ - if (this.isNonRenderableFile(relativeSourceFile)) - return await copyFile(sourceFile, targetFile); + + if (this.isNonRenderableFile(relativeSourceFile)) return await copyFile(sourceFile, targetFile); await this.renderAndWriteToFile(asyncapiDocument, sourceFile, targetFile); } @@ -1181,12 +922,12 @@ class Generator { */ maybeRenameSourceFile(sourceFile) { switch (path.basename(sourceFile)) { - case GIT_IGNORE_FILENAME: - return path.join(path.dirname(sourceFile), ".gitignore"); - case NPM_IGNORE_FILENAME: - return path.join(path.dirname(sourceFile), ".npmignore"); - default: - return sourceFile; + case GIT_IGNORE_FILENAME: + return path.join(path.dirname(sourceFile), '.gitignore'); + case NPM_IGNORE_FILENAME: + return path.join(path.dirname(sourceFile), '.npmignore'); + default: + return sourceFile; } } @@ -1201,28 +942,10 @@ class Generator { */ async renderFile(asyncapiDocument, filePath, extraTemplateData = {}) { if (isReactTemplate(this.templateConfig)) { - return await renderReact( - asyncapiDocument, - filePath, - extraTemplateData, - this.templateDir, - this.templateContentDir, - TRANSPILED_TEMPLATE_LOCATION, - this.templateParams, - this.debug, - this.originalAsyncAPI - ); + return await renderReact(asyncapiDocument, filePath, extraTemplateData, this.templateDir, this.templateContentDir, TRANSPILED_TEMPLATE_LOCATION, this.templateParams, this.debug, this.originalAsyncAPI); } - const templateString = await readFile(filePath, "utf8"); - return renderNunjucks( - asyncapiDocument, - templateString, - filePath, - extraTemplateData, - this.templateParams, - this.originalAsyncAPI, - this.nunjucks - ); + const templateString = await readFile(filePath, 'utf8'); + return renderNunjucks(asyncapiDocument, templateString, filePath, extraTemplateData, this.templateParams, this.originalAsyncAPI, this.nunjucks); } /** @@ -1234,11 +957,10 @@ class Generator { */ isNonRenderableFile(fileName) { const nonRenderableFiles = this.templateConfig.nonRenderableFiles || []; - return ( - Array.isArray(nonRenderableFiles) && - (nonRenderableFiles.some((globExp) => minimatch(fileName, globExp)) || - (isReactTemplate(this.templateConfig) && !isJsFile(fileName))) - ); + if (!Array.isArray(nonRenderableFiles)) return false; + if (nonRenderableFiles.some(globExp => minimatch(fileName, globExp))) return true; + if (isReactTemplate(this.templateConfig) && !isJsFile(fileName)) return true; + return false; } /** @@ -1253,9 +975,7 @@ class Generator { const fileExists = await exists(path.resolve(this.targetDir, filePath)); if (!fileExists) return true; - return !this.noOverwriteGlobs.some((globExp) => - minimatch(filePath, globExp) - ); + return !this.noOverwriteGlobs.some(globExp => minimatch(filePath, globExp)); } /** @@ -1270,7 +990,7 @@ class Generator { return; } - const json = await readFile(configPath, { encoding: "utf8" }); + const json = await readFile(configPath, { encoding: 'utf8' }); const generatorProp = JSON.parse(json).generator; this.templateConfig = generatorProp || {}; } catch (e) { @@ -1286,20 +1006,16 @@ class Generator { */ async loadDefaultValues() { const parameters = this.templateConfig.parameters; - const defaultValues = Object.keys(parameters || {}).filter( - (key) => parameters[key].default - ); + const defaultValues = Object.keys(parameters || {}).filter(key => parameters[key].default); - defaultValues - .filter((dv) => this.templateParams[dv] === undefined) - .forEach((dv) => - Object.defineProperty(this.templateParams, dv, { - enumerable: true, - get() { - return parameters[dv].default; - }, - }) - ); + defaultValues.filter(dv => this.templateParams[dv] === undefined).forEach(dv => + Object.defineProperty(this.templateParams, dv, { + enumerable: true, + get() { + return parameters[dv].default; + } + }) + ); } /** @@ -1312,17 +1028,15 @@ class Generator { async launchHook(hookName, hookArguments) { let disabledHooks = this.disabledHooks[hookName] || []; if (disabledHooks === true) return; - if (typeof disabledHooks === "string") disabledHooks = [disabledHooks]; + if (typeof disabledHooks === 'string') disabledHooks = [disabledHooks]; const hooks = this.hooks[hookName]; if (!Array.isArray(hooks)) return; - const promises = hooks - .map(async (hook) => { - if (typeof hook !== "function") return; - if (disabledHooks.includes(hook.name)) return; - return await hook(this, hookArguments); - }) - .filter(Boolean); + const promises = hooks.map(async (hook) => { + if (typeof hook !== 'function') return; + if (disabledHooks.includes(hook.name)) return; + return await hook(this, hookArguments); + }).filter(Boolean); return Promise.all(promises); } @@ -1336,30 +1050,27 @@ class Generator { isHookAvailable(hookName) { const hooks = this.hooks[hookName]; - if ( - this.disabledHooks[hookName] === true || - !Array.isArray(hooks) || - hooks.length === 0 - ) - return false; + if (this.disabledHooks[hookName] === true + || !Array.isArray(hooks) + || hooks.length === 0) return false; let disabledHooks = this.disabledHooks[hookName] || []; - if (typeof disabledHooks === "string") disabledHooks = [disabledHooks]; + if (typeof disabledHooks === 'string') disabledHooks = [disabledHooks]; - return !!hooks.filter((h) => !disabledHooks.includes(h.name)).length; + return !!hooks.filter(h => !disabledHooks.includes(h.name)).length; } /** * Check if given directory is a git repo with unstaged changes and is not in .gitignore or is not empty * @private * @param {String} dir Directory that needs to be tested for a given condition. - */ + */ async verifyTargetDir(dir) { const isGitRepo = await git(dir).checkIsRepo(); if (isGitRepo) { //Need to figure out root of the repository to properly verify .gitignore - const root = await git(dir).revparse(["--show-toplevel"]); + const root = await git(dir).revparse(['--show-toplevel']); const gitInfo = git(root); //Skipping verification if workDir inside repo is declared in .gitignore @@ -1375,20 +1086,13 @@ class Generator { const stagedFiles = gitStatus.staged; const modifiedFiles = gitStatus.modified; - const hasModifiedUstagedFiles = - modifiedFiles.filter((e) => stagedFiles.indexOf(e) === -1).length !== 0; + const hasModifiedUstagedFiles = (modifiedFiles.filter(e => stagedFiles.indexOf(e) === -1)).length !== 0; - if (hasModifiedUstagedFiles || hasUntrackedUnstagedFiles) - throw new Error( - `"${this.targetDir}" is in a git repository with unstaged changes. Please commit your changes before proceeding or add proper directory to .gitignore file. You can also use the --force-write flag to skip this rule (not recommended).` - ); + if (hasModifiedUstagedFiles || hasUntrackedUnstagedFiles) throw new Error(`"${this.targetDir}" is in a git repository with unstaged changes. Please commit your changes before proceeding or add proper directory to .gitignore file. You can also use the --force-write flag to skip this rule (not recommended).`); } else { const isDirEmpty = (await readDir(dir)).length === 0; - if (!isDirEmpty) - throw new Error( - `"${this.targetDir}" is not an empty directory. You might override your work. To skip this rule, please make your code a git repository or use the --force-write flag (not recommended).` - ); + if (!isDirEmpty) throw new Error(`"${this.targetDir}" is not an empty directory. You might override your work. To skip this rule, please make your code a git repository or use the --force-write flag (not recommended).`); } } }