diff --git a/apps/generator-cli/src/app/mocks/command.mock.ts b/apps/generator-cli/src/app/mocks/command.mock.ts index 8db8e9a2e96..73d012411fc 100644 --- a/apps/generator-cli/src/app/mocks/command.mock.ts +++ b/apps/generator-cli/src/app/mocks/command.mock.ts @@ -1,7 +1,4 @@ -import { get, set } from 'lodash'; - export class CommandMock { - commands: { [key: string]: { self: CommandMock, @@ -24,41 +21,55 @@ export class CommandMock { helpInformation = jest.fn().mockReturnValue('some help text'); - action = jest.fn().mockImplementation(action => { - set(this.commands, [this.currentCommand, 'action'], action); + ensureCommand = () => { + if (!this.commands[this.currentCommand]) { + this.commands[this.currentCommand] = { + self: this, + description: '', + allowUnknownOption: false, + action: () => {}, + options: [], + }; + } + }; + + action = jest.fn().mockImplementation((action) => { + this.ensureCommand(); + this.commands[this.currentCommand].action = action; return this; }); option = jest.fn().mockImplementation((flags, description, defaultValue) => { - const options = get(this.commands, [this.currentCommand, 'options'], []); - - set(this.commands, [this.currentCommand, 'options'], [ + const options = this.commands[this.currentCommand]?.options ?? []; + this.ensureCommand(); + this.commands[this.currentCommand].options = [ ...options, { flags, description, - defaultValue - } - ]); + defaultValue, + }, + ]; return this; }); - command = jest.fn().mockImplementation(cmd => { + command = jest.fn().mockImplementation((cmd) => { this.currentCommand = cmd; this.refs[cmd] = this; return this; }); allowUnknownOption = jest.fn().mockImplementation(() => { - set(this.commands, [this.currentCommand, 'allowUnknownOption'], true); + this.ensureCommand(); + this.commands[this.currentCommand].allowUnknownOption = true; return this; }); - description = jest.fn().mockImplementation(desc => { - set(this.commands, [this.currentCommand, 'description'], desc); + description = jest.fn().mockImplementation((desc) => { + this.ensureCommand(); + this.commands[this.currentCommand].description = desc; return this; }); opts = jest.fn(); - } diff --git a/apps/generator-cli/src/app/services/config.service.ts b/apps/generator-cli/src/app/services/config.service.ts index f968eadd08a..5a0cc2dadda 100644 --- a/apps/generator-cli/src/app/services/config.service.ts +++ b/apps/generator-cli/src/app/services/config.service.ts @@ -1,7 +1,6 @@ -import {Inject, Injectable} from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import * as path from 'path'; -import {COMMANDER_PROGRAM, LOGGER} from '../constants'; -import {set, get, has, merge} from 'lodash'; +import { COMMANDER_PROGRAM, LOGGER } from '../constants'; import * as fs from 'fs-extra'; import { Command } from 'commander'; @@ -45,29 +44,106 @@ export class ConfigService { } get(path: string, defaultValue?: T): T { - return get(this.read(), path, defaultValue) + const getPath = ( + obj: Record | unknown, + keys: string[], + ): unknown => { + if (!obj || keys.length === 0) return obj; + + const [head, ...tail] = keys; + + if (tail.length === 0) { + return obj[head]; + } + + return getPath(obj[head], tail); + }; + + const result = getPath(this.read(), path.split('.')) as T; + return result !== undefined ? result : defaultValue; } - has(path) { - return has(this.read(), path) + has(path: string) { + const hasPath = ( + obj: Record | unknown, + keys: string[], + ): boolean => { + if (!obj || keys.length === 0) return false; + + const [head, ...tail] = keys; + + if (tail.length === 0) { + return Object.prototype.hasOwnProperty.call(obj, head); + } + + return hasPath(obj[head] as Record, tail); + }; + + return hasPath(this.read(), path.split('.')); } set(path: string, value: unknown) { - this.write(set(this.read(), path, value)) - return this + const setPath = ( + obj: object, + keys: string[], + val: unknown, + ): object => { + const [head, ...tail] = keys; + + if (tail.length === 0) { + obj[head] = val; + return obj; + } + + if (!obj[head] || typeof obj[head] !== 'object') { + obj[head] = {}; + } + + setPath(obj[head] as Record, tail, val); + return obj; + }; + + const config = this.read(); + this.write(setPath(config, path.split('.'), value)); + return this; } private read() { - fs.ensureFileSync(this.configFile) - - return merge( + const deepMerge = ( + target: object, + source: object, + ): object => { + if (!source || typeof source !== 'object') return target; + + const result = { ...target }; + + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + if ( + source[key] && + typeof source[key] === 'object' && + !Array.isArray(source[key]) + ) { + const value = (result[key] || {}); + result[key] = deepMerge(value, source[key]); + } else { + result[key] = source[key]; + } + } + } + + return result; + }; + + fs.ensureFileSync(this.configFile); + + return deepMerge( this.defaultConfig, - fs.readJSONSync(this.configFile, {throws: false, encoding: 'utf8'}), - ) + fs.readJSONSync(this.configFile, { throws: false, encoding: 'utf8' }), + ); } private write(config) { fs.writeJSONSync(this.configFile, config, {encoding: 'utf8', spaces: config.spaces || 2}) } - } diff --git a/apps/generator-cli/src/app/services/generator.service.ts b/apps/generator-cli/src/app/services/generator.service.ts index 9eefe98d577..83abc0f8b0c 100644 --- a/apps/generator-cli/src/app/services/generator.service.ts +++ b/apps/generator-cli/src/app/services/generator.service.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { flatten, isString, kebabCase, sortBy, upperFirst } from 'lodash'; import concurrently, { type CloseEvent } from 'concurrently'; import * as path from 'path'; @@ -63,8 +62,8 @@ export class GeneratorService { const globsWithNoMatches = []; - const commands = flatten( - enabledGenerators.map(([name, config]) => { + const commands = enabledGenerators + .map(([name, config]) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { glob: globPattern, disabled, ...params } = config; @@ -88,7 +87,7 @@ export class GeneratorService { command: this.buildCommand(cwd, params, customGenerator, spec), })); }) - ); + .flat(); const generated = commands.length > 0 && @@ -112,18 +111,21 @@ export class GeneratorService { return generated; } - private printResult(res: CloseEvent[]) { - this.logger.log( - sortBy(res, 'command.name') - .map(({ exitCode, command }) => { - const failed = typeof exitCode === 'string' || exitCode > 0; - return [ - chalk[failed ? 'red' : 'green'](command.name), - ...(failed ? [chalk.yellow(` ${command.command}\n`)] : []), - ].join('\n'); - }) - .join('\n') - ); + private printResult(res?: CloseEvent[]) { + if (res) { + this.logger.log( + res + .sort((a, b) => a.command.name.localeCompare(b.command.name)) + .map(({ exitCode, command }) => { + const failed = typeof exitCode === 'string' || exitCode > 0; + return [ + chalk[failed ? 'red' : 'green'](command.name), + ...(failed ? [chalk.yellow(` ${command.command}\n`)] : []), + ].join('\n'); + }) + .join('\n'), + ); + } } private buildCommand( @@ -142,7 +144,7 @@ export class GeneratorService { const placeholders: { [key: string]: string } = { name, - Name: upperFirst(name), + Name: name.charAt(0).toUpperCase() + name.slice(1), cwd, @@ -160,7 +162,11 @@ export class GeneratorService { ...params, }) .map(([k, v]) => { - const key = kebabCase(k); + const key = k + .replace(/([a-z])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase(); + const value = (() => { switch (typeof v) { case 'object': @@ -242,7 +248,7 @@ export class GeneratorService { )}" org.openapitools.codegen.OpenAPIGenerator` : `-jar "${cliPath}"`; return ['java', process.env['JAVA_OPTS'], subCmd, 'generate', appendix] - .filter(isString) + .filter((str): str is string => str != null && typeof str === 'string') .join(' '); }; diff --git a/apps/generator-cli/src/app/services/pass-through.service.ts b/apps/generator-cli/src/app/services/pass-through.service.ts index 304ec48249d..920d629ac34 100644 --- a/apps/generator-cli/src/app/services/pass-through.service.ts +++ b/apps/generator-cli/src/app/services/pass-through.service.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import chalk from 'chalk'; import { exec, spawn } from 'child_process'; import { Command } from 'commander'; -import { isString, startsWith, trim } from 'lodash'; import { COMMANDER_PROGRAM, LOGGER } from '../constants'; import { GeneratorService } from './generator.service'; import { VersionManagerService } from './version-manager.service'; @@ -101,14 +100,18 @@ export class PassThroughService { const commands = help .split('\n') - .filter((line) => startsWith(line, ' ')) - .map(trim) - .map((line) => line.match(/^([a-z-]+)\s+(.+)/i).slice(1)) + .filter((line) => line.startsWith(' ')) + .map((line) => + line + .trim() + .match(/^([a-z-]+)\s+(.+)/i) + .slice(1), + ) .reduce((acc, [cmd, desc]) => ({ ...acc, [cmd]: desc }), {}); const allCommands = completion .split('\n') - .map(trim) + .map((line) => line.trim()) .filter((c) => c.length > 0 && c.indexOf('--') !== 0); for (const cmd of allCommands) { @@ -143,7 +146,7 @@ export class PassThroughService { : `-jar "${cliPath}"`; return ['java', process.env['JAVA_OPTS'], subCmd] - .filter(isString) + .filter((str): str is string => str != null && typeof str === 'string') .join(' '); } diff --git a/apps/generator-cli/src/app/services/version-manager.service.ts b/apps/generator-cli/src/app/services/version-manager.service.ts index 84ccd7cc84f..29722ba5f5e 100644 --- a/apps/generator-cli/src/app/services/version-manager.service.ts +++ b/apps/generator-cli/src/app/services/version-manager.service.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { catchError, map, switchMap } from 'rxjs/operators'; -import { replace } from 'lodash'; import { Observable, of } from 'rxjs'; import { AxiosError } from 'axios'; import * as fs from 'fs-extra'; @@ -251,8 +250,8 @@ export class VersionManagerService { private replacePlaceholders(str: string, additionalPlaceholders = {}) { const placeholders = { ...additionalPlaceholders, - groupId: replace(mvn.groupId, '.', '/'), - artifactId: replace(mvn.artifactId, '.', '/'), + groupId: mvn.groupId.replace(/\./g, '/'), + artifactId: mvn.artifactId.replace(/\./g, '/'), 'group.id': mvn.groupId, 'artifact.id': mvn.artifactId, }; diff --git a/apps/generator-cli/webpack.config.js b/apps/generator-cli/webpack.config.js index d57bffe1b24..e8308da11b9 100644 --- a/apps/generator-cli/webpack.config.js +++ b/apps/generator-cli/webpack.config.js @@ -4,7 +4,6 @@ const { composePlugins, withNx } = require('@nx/webpack'); const { name, version, ...packageConfig } = require('../../package.json'); const GeneratePackageJsonPlugin = require('generate-package-json-webpack-plugin'); const { BannerPlugin } = require('webpack'); -const { omit } = require('lodash'); // Nx plugins for webpack. module.exports = composePlugins( @@ -12,8 +11,13 @@ module.exports = composePlugins( target: 'node', }), (config) => { + const packageConfigWithoutScriptsAndDeps = {...packageConfig}; + delete packageConfigWithoutScriptsAndDeps.devDependencies; + delete packageConfigWithoutScriptsAndDeps.dependencies; + delete packageConfigWithoutScriptsAndDeps.scripts; + const basePackageValues = { - ...omit(packageConfig, ['scripts', 'dependencies', 'devDependencies']), + ...packageConfigWithoutScriptsAndDeps, version, name: `@${name}/openapi-generator-cli`, description: diff --git a/package.json b/package.json index 465285e6901..ed2a5a5af15 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,6 @@ "glob": "11.x", "inquirer": "8.2.6", "jsonpath": "1.1.1", - "lodash": "4.17.21", "proxy-agent": "^6.4.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", @@ -118,7 +117,6 @@ "@types/inquirer": "8.2.11", "@types/jest": "29.5.14", "@types/jsonpath": "0.2.4", - "@types/lodash": "4.17.20", "@types/node": "20.14.8", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", diff --git a/yarn.lock b/yarn.lock index 44bde31024b..0234f960b66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2805,11 +2805,6 @@ resolved "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz#065be59981c1420832835af656377622271154be" integrity sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA== -"@types/lodash@4.17.20": - version "4.17.20" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93" - integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA== - "@types/mime@^1": version "1.3.5" resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -7568,7 +7563,7 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== -lodash@4.17.21, lodash@^4.17.21, lodash@^4.17.4: +lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==