Skip to content

Commit dceaac1

Browse files
authored
chore: remove lodash dependency and refactor code to eliminate its usage (#958)
1 parent e517c31 commit dceaac1

File tree

8 files changed

+160
-68
lines changed

8 files changed

+160
-68
lines changed
Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { get, set } from 'lodash';
2-
31
export class CommandMock {
4-
52
commands: {
63
[key: string]: {
74
self: CommandMock,
@@ -24,41 +21,55 @@ export class CommandMock {
2421

2522
helpInformation = jest.fn().mockReturnValue('some help text');
2623

27-
action = jest.fn().mockImplementation(action => {
28-
set(this.commands, [this.currentCommand, 'action'], action);
24+
ensureCommand = () => {
25+
if (!this.commands[this.currentCommand]) {
26+
this.commands[this.currentCommand] = {
27+
self: this,
28+
description: '',
29+
allowUnknownOption: false,
30+
action: () => {},
31+
options: [],
32+
};
33+
}
34+
};
35+
36+
action = jest.fn().mockImplementation((action) => {
37+
this.ensureCommand();
38+
this.commands[this.currentCommand].action = action;
2939
return this;
3040
});
3141

3242
option = jest.fn().mockImplementation((flags, description, defaultValue) => {
33-
const options = get(this.commands, [this.currentCommand, 'options'], []);
34-
35-
set(this.commands, [this.currentCommand, 'options'], [
43+
const options = this.commands[this.currentCommand]?.options ?? [];
44+
this.ensureCommand();
45+
this.commands[this.currentCommand].options = [
3646
...options,
3747
{
3848
flags,
3949
description,
40-
defaultValue
41-
}
42-
]);
50+
defaultValue,
51+
},
52+
];
4353
return this;
4454
});
4555

46-
command = jest.fn().mockImplementation(cmd => {
56+
command = jest.fn().mockImplementation((cmd) => {
4757
this.currentCommand = cmd;
4858
this.refs[cmd] = this;
4959
return this;
5060
});
5161

5262
allowUnknownOption = jest.fn().mockImplementation(() => {
53-
set(this.commands, [this.currentCommand, 'allowUnknownOption'], true);
63+
this.ensureCommand();
64+
this.commands[this.currentCommand].allowUnknownOption = true;
5465
return this;
5566
});
5667

57-
description = jest.fn().mockImplementation(desc => {
58-
set(this.commands, [this.currentCommand, 'description'], desc);
68+
description = jest.fn().mockImplementation((desc) => {
69+
this.ensureCommand();
70+
this.commands[this.currentCommand].description = desc;
5971
return this;
6072
});
6173

6274
opts = jest.fn();
63-
6475
}

apps/generator-cli/src/app/services/config.service.ts

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import {Inject, Injectable} from '@nestjs/common';
1+
import { Inject, Injectable } from '@nestjs/common';
22
import * as path from 'path';
3-
import {COMMANDER_PROGRAM, LOGGER} from '../constants';
4-
import {set, get, has, merge} from 'lodash';
3+
import { COMMANDER_PROGRAM, LOGGER } from '../constants';
54
import * as fs from 'fs-extra';
65
import { Command } from 'commander';
76

@@ -45,29 +44,106 @@ export class ConfigService {
4544
}
4645

4746
get<T = unknown>(path: string, defaultValue?: T): T {
48-
return get(this.read(), path, defaultValue)
47+
const getPath = (
48+
obj: Record<string, unknown> | unknown,
49+
keys: string[],
50+
): unknown => {
51+
if (!obj || keys.length === 0) return obj;
52+
53+
const [head, ...tail] = keys;
54+
55+
if (tail.length === 0) {
56+
return obj[head];
57+
}
58+
59+
return getPath(obj[head], tail);
60+
};
61+
62+
const result = getPath(this.read(), path.split('.')) as T;
63+
return result !== undefined ? result : defaultValue;
4964
}
5065

51-
has(path) {
52-
return has(this.read(), path)
66+
has(path: string) {
67+
const hasPath = (
68+
obj: Record<string, unknown> | unknown,
69+
keys: string[],
70+
): boolean => {
71+
if (!obj || keys.length === 0) return false;
72+
73+
const [head, ...tail] = keys;
74+
75+
if (tail.length === 0) {
76+
return Object.prototype.hasOwnProperty.call(obj, head);
77+
}
78+
79+
return hasPath(obj[head] as Record<string, unknown>, tail);
80+
};
81+
82+
return hasPath(this.read(), path.split('.'));
5383
}
5484

5585
set(path: string, value: unknown) {
56-
this.write(set(this.read(), path, value))
57-
return this
86+
const setPath = (
87+
obj: object,
88+
keys: string[],
89+
val: unknown,
90+
): object => {
91+
const [head, ...tail] = keys;
92+
93+
if (tail.length === 0) {
94+
obj[head] = val;
95+
return obj;
96+
}
97+
98+
if (!obj[head] || typeof obj[head] !== 'object') {
99+
obj[head] = {};
100+
}
101+
102+
setPath(obj[head] as Record<string, unknown>, tail, val);
103+
return obj;
104+
};
105+
106+
const config = this.read();
107+
this.write(setPath(config, path.split('.'), value));
108+
return this;
58109
}
59110

60111
private read() {
61-
fs.ensureFileSync(this.configFile)
62-
63-
return merge(
112+
const deepMerge = (
113+
target: object,
114+
source: object,
115+
): object => {
116+
if (!source || typeof source !== 'object') return target;
117+
118+
const result = { ...target };
119+
120+
for (const key in source) {
121+
if (Object.prototype.hasOwnProperty.call(source, key)) {
122+
if (
123+
source[key] &&
124+
typeof source[key] === 'object' &&
125+
!Array.isArray(source[key])
126+
) {
127+
const value = (result[key] || {});
128+
result[key] = deepMerge(value, source[key]);
129+
} else {
130+
result[key] = source[key];
131+
}
132+
}
133+
}
134+
135+
return result;
136+
};
137+
138+
fs.ensureFileSync(this.configFile);
139+
140+
return deepMerge(
64141
this.defaultConfig,
65-
fs.readJSONSync(this.configFile, {throws: false, encoding: 'utf8'}),
66-
)
142+
fs.readJSONSync(this.configFile, { throws: false, encoding: 'utf8' }),
143+
);
67144
}
68145

69146
private write(config) {
70147
fs.writeJSONSync(this.configFile, config, {encoding: 'utf8', spaces: config.spaces || 2})
71148
}
72-
73149
}

apps/generator-cli/src/app/services/generator.service.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Inject, Injectable } from '@nestjs/common';
2-
import { flatten, isString, kebabCase, sortBy, upperFirst } from 'lodash';
32

43
import concurrently, { type CloseEvent } from 'concurrently';
54
import * as path from 'path';
@@ -63,8 +62,8 @@ export class GeneratorService {
6362

6463
const globsWithNoMatches = [];
6564

66-
const commands = flatten(
67-
enabledGenerators.map(([name, config]) => {
65+
const commands = enabledGenerators
66+
.map(([name, config]) => {
6867
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6968
const { glob: globPattern, disabled, ...params } = config;
7069

@@ -88,7 +87,7 @@ export class GeneratorService {
8887
command: this.buildCommand(cwd, params, customGenerator, spec),
8988
}));
9089
})
91-
);
90+
.flat();
9291

9392
const generated =
9493
commands.length > 0 &&
@@ -112,18 +111,21 @@ export class GeneratorService {
112111
return generated;
113112
}
114113

115-
private printResult(res: CloseEvent[]) {
116-
this.logger.log(
117-
sortBy(res, 'command.name')
118-
.map(({ exitCode, command }) => {
119-
const failed = typeof exitCode === 'string' || exitCode > 0;
120-
return [
121-
chalk[failed ? 'red' : 'green'](command.name),
122-
...(failed ? [chalk.yellow(` ${command.command}\n`)] : []),
123-
].join('\n');
124-
})
125-
.join('\n')
126-
);
114+
private printResult(res?: CloseEvent[]) {
115+
if (res) {
116+
this.logger.log(
117+
res
118+
.sort((a, b) => a.command.name.localeCompare(b.command.name))
119+
.map(({ exitCode, command }) => {
120+
const failed = typeof exitCode === 'string' || exitCode > 0;
121+
return [
122+
chalk[failed ? 'red' : 'green'](command.name),
123+
...(failed ? [chalk.yellow(` ${command.command}\n`)] : []),
124+
].join('\n');
125+
})
126+
.join('\n'),
127+
);
128+
}
127129
}
128130

129131
private buildCommand(
@@ -142,7 +144,7 @@ export class GeneratorService {
142144

143145
const placeholders: { [key: string]: string } = {
144146
name,
145-
Name: upperFirst(name),
147+
Name: name.charAt(0).toUpperCase() + name.slice(1),
146148

147149
cwd,
148150

@@ -160,7 +162,11 @@ export class GeneratorService {
160162
...params,
161163
})
162164
.map(([k, v]) => {
163-
const key = kebabCase(k);
165+
const key = k
166+
.replace(/([a-z])([A-Z])/g, '$1-$2')
167+
.replace(/[\s_]+/g, '-')
168+
.toLowerCase();
169+
164170
const value = (() => {
165171
switch (typeof v) {
166172
case 'object':
@@ -242,7 +248,7 @@ export class GeneratorService {
242248
)}" org.openapitools.codegen.OpenAPIGenerator`
243249
: `-jar "${cliPath}"`;
244250
return ['java', process.env['JAVA_OPTS'], subCmd, 'generate', appendix]
245-
.filter(isString)
251+
.filter((str): str is string => str != null && typeof str === 'string')
246252
.join(' ');
247253
};
248254

apps/generator-cli/src/app/services/pass-through.service.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common';
22
import chalk from 'chalk';
33
import { exec, spawn } from 'child_process';
44
import { Command } from 'commander';
5-
import { isString, startsWith, trim } from 'lodash';
65
import { COMMANDER_PROGRAM, LOGGER } from '../constants';
76
import { GeneratorService } from './generator.service';
87
import { VersionManagerService } from './version-manager.service';
@@ -101,14 +100,18 @@ export class PassThroughService {
101100

102101
const commands = help
103102
.split('\n')
104-
.filter((line) => startsWith(line, ' '))
105-
.map<string>(trim)
106-
.map((line) => line.match(/^([a-z-]+)\s+(.+)/i).slice(1))
103+
.filter((line) => line.startsWith(' '))
104+
.map((line) =>
105+
line
106+
.trim()
107+
.match(/^([a-z-]+)\s+(.+)/i)
108+
.slice(1),
109+
)
107110
.reduce((acc, [cmd, desc]) => ({ ...acc, [cmd]: desc }), {});
108111

109112
const allCommands = completion
110113
.split('\n')
111-
.map<string>(trim)
114+
.map<string>((line) => line.trim())
112115
.filter((c) => c.length > 0 && c.indexOf('--') !== 0);
113116

114117
for (const cmd of allCommands) {
@@ -143,7 +146,7 @@ export class PassThroughService {
143146
: `-jar "${cliPath}"`;
144147

145148
return ['java', process.env['JAVA_OPTS'], subCmd]
146-
.filter(isString)
149+
.filter((str): str is string => str != null && typeof str === 'string')
147150
.join(' ');
148151
}
149152

apps/generator-cli/src/app/services/version-manager.service.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Inject, Injectable } from '@nestjs/common';
22
import { HttpService } from '@nestjs/axios';
33
import { catchError, map, switchMap } from 'rxjs/operators';
4-
import { replace } from 'lodash';
54
import { Observable, of } from 'rxjs';
65
import { AxiosError } from 'axios';
76
import * as fs from 'fs-extra';
@@ -251,8 +250,8 @@ export class VersionManagerService {
251250
private replacePlaceholders(str: string, additionalPlaceholders = {}) {
252251
const placeholders = {
253252
...additionalPlaceholders,
254-
groupId: replace(mvn.groupId, '.', '/'),
255-
artifactId: replace(mvn.artifactId, '.', '/'),
253+
groupId: mvn.groupId.replace(/\./g, '/'),
254+
artifactId: mvn.artifactId.replace(/\./g, '/'),
256255
'group.id': mvn.groupId,
257256
'artifact.id': mvn.artifactId,
258257
};

apps/generator-cli/webpack.config.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@ const { composePlugins, withNx } = require('@nx/webpack');
44
const { name, version, ...packageConfig } = require('../../package.json');
55
const GeneratePackageJsonPlugin = require('generate-package-json-webpack-plugin');
66
const { BannerPlugin } = require('webpack');
7-
const { omit } = require('lodash');
87

98
// Nx plugins for webpack.
109
module.exports = composePlugins(
1110
withNx({
1211
target: 'node',
1312
}),
1413
(config) => {
14+
const packageConfigWithoutScriptsAndDeps = {...packageConfig};
15+
delete packageConfigWithoutScriptsAndDeps.devDependencies;
16+
delete packageConfigWithoutScriptsAndDeps.dependencies;
17+
delete packageConfigWithoutScriptsAndDeps.scripts;
18+
1519
const basePackageValues = {
16-
...omit(packageConfig, ['scripts', 'dependencies', 'devDependencies']),
20+
...packageConfigWithoutScriptsAndDeps,
1721
version,
1822
name: `@${name}/openapi-generator-cli`,
1923
description:

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
"glob": "11.x",
9595
"inquirer": "8.2.6",
9696
"jsonpath": "1.1.1",
97-
"lodash": "4.17.21",
9897
"proxy-agent": "^6.4.0",
9998
"reflect-metadata": "^0.2.2",
10099
"rxjs": "^7.8.2",
@@ -118,7 +117,6 @@
118117
"@types/inquirer": "8.2.11",
119118
"@types/jest": "29.5.14",
120119
"@types/jsonpath": "0.2.4",
121-
"@types/lodash": "4.17.20",
122120
"@types/node": "20.14.8",
123121
"@typescript-eslint/eslint-plugin": "7.18.0",
124122
"@typescript-eslint/parser": "7.18.0",

0 commit comments

Comments
 (0)