Skip to content

Commit 7bd8d6f

Browse files
committed
chore: replace lodash.mergewith with ts-deepmerge
* refactor: remove lodash.mergewith dependency from package.json and bun.lock * feat: update merge logic in user-metadata.service.ts and mock.setup.ts to use ts-deepmerge * refactor: adjust mock setup in tests to eliminate lodash.mergewith usage
1 parent 9c6f329 commit 7bd8d6f

File tree

7 files changed

+92
-64
lines changed

7 files changed

+92
-64
lines changed

bun.lock

Lines changed: 4 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
"exponential-backoff": "^3.1.2",
1212
"express": "^4.17.1",
1313
"helmet": "^7.0.0",
14-
"lodash.mergewith": "^4.6.2",
1514
"mongodb": "6.3",
1615
"morgan": "^1.10.0",
1716
"p-limit": "^7.2.0",
1817
"rrule": "^2.7.2",
1918
"saslprep": "^1.0.3",
2019
"supertokens-node": "^23.0.1",
20+
"ts-deepmerge": "^7.0.3",
2121
"tslib": "^2.4.0"
2222
},
2323
"devDependencies": {
@@ -27,7 +27,6 @@
2727
"@types/cors": "^2.8.12",
2828
"@types/express": "^4.17.13",
2929
"@types/jest": "^29.0.0",
30-
"@types/lodash.mergewith": "^4.6.9",
3130
"@types/morgan": "^1.9.4",
3231
"@types/node": "^22.13.10",
3332
"@types/supertest": "^6.0.3",

packages/backend/src/__tests__/helpers/mock.setup.ts

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { type Handler, type NextFunction, type Response } from "express";
2-
import mergeWith from "lodash.mergewith";
32
import { randomUUID } from "node:crypto";
43
import { type SessionRequest } from "supertokens-node/framework/express";
54
import {
@@ -211,12 +210,20 @@ function mockSuperTokens() {
211210
mockModule(
212211
"supertokens-node",
213212
(superTokens: typeof import("supertokens-node")) => {
214-
const superTokensModule = mergeWith(superTokens, {
213+
const mock = {
215214
getUserIdMapping: jest.fn(getUserIdMapping),
216215
createUserIdMapping: jest.fn(createUserIdMapping),
217-
});
216+
default: superTokens,
217+
};
218218

219-
return mergeWith(superTokensModule, { default: superTokensModule });
219+
for (const key in mock) {
220+
try {
221+
(superTokens as any)[key] = (mock as any)[key];
222+
} catch (e) {
223+
// skip read-only
224+
}
225+
}
226+
return superTokens;
220227
},
221228
);
222229

@@ -225,13 +232,19 @@ function mockSuperTokens() {
225232
(
226233
frameworkExpress: typeof import("supertokens-node/recipe/session/framework/express"),
227234
) => {
228-
const frameworkExpressModule = mergeWith(frameworkExpress, {
235+
const mock = {
229236
verifySession: jest.fn(verifySession),
230-
});
237+
default: frameworkExpress,
238+
};
231239

232-
return mergeWith(frameworkExpressModule, {
233-
default: frameworkExpressModule,
234-
});
240+
for (const key in mock) {
241+
try {
242+
(frameworkExpress as any)[key] = (mock as any)[key];
243+
} catch (e) {
244+
// skip read-only
245+
}
246+
}
247+
return frameworkExpress;
235248
},
236249
);
237250

@@ -240,12 +253,20 @@ function mockSuperTokens() {
240253
(
241254
recipeUserMetadata: typeof import("supertokens-node/recipe/usermetadata"),
242255
) => {
243-
const userMetadataModule = mergeWith(recipeUserMetadata, {
256+
const mock = {
244257
updateUserMetadata: jest.fn(updateUserMetadata),
245258
getUserMetadata: jest.fn(getUserMetadata),
246-
});
259+
default: recipeUserMetadata,
260+
};
247261

248-
return mergeWith(userMetadataModule, { default: userMetadataModule });
262+
for (const key in mock) {
263+
try {
264+
(recipeUserMetadata as any)[key] = (mock as any)[key];
265+
} catch (e) {
266+
// skip read-only
267+
}
268+
}
269+
return recipeUserMetadata;
249270
},
250271
);
251272

@@ -257,34 +278,35 @@ function mockSuperTokens() {
257278
const getInstanceOrThrowError =
258279
session.default.getInstanceOrThrowError.bind(session.default);
259280

260-
const sessionModule = mergeWith(session, {
261-
default: mergeWith(session.default, {
262-
getInstanceOrThrowError: jest.fn(() => {
263-
const instance = getInstanceOrThrowError();
264-
265-
return mergeWith(instance, {
266-
apiImpl: mergeWith(instance.apiImpl, {
267-
verifySession: jest.fn(
268-
async (input: {
269-
verifySessionOptions: VerifySessionOptions | undefined;
270-
options: APIOptions;
271-
userContext: UserContext;
272-
}) => {
273-
const req = input.options.req as ExpressRequest;
274-
const res = input.options.res as ExpressResponse;
275-
276-
verifySession(input)(req.original, res.original);
277-
278-
return Promise.resolve(req.original.session);
279-
},
280-
),
281-
}),
282-
});
283-
}),
284-
}),
285-
});
286-
287-
return sessionModule;
281+
try {
282+
session.default.getInstanceOrThrowError = jest.fn(() => {
283+
const instance = getInstanceOrThrowError();
284+
285+
try {
286+
instance.apiImpl.verifySession = jest.fn(
287+
async (input: {
288+
verifySessionOptions: VerifySessionOptions | undefined;
289+
options: APIOptions;
290+
userContext: UserContext;
291+
}) => {
292+
const req = input.options.req as ExpressRequest;
293+
const res = input.options.res as ExpressResponse;
294+
295+
verifySession(input)(req.original, res.original);
296+
297+
return Promise.resolve(req.original.session);
298+
},
299+
);
300+
} catch (e) {
301+
// ignore
302+
}
303+
return instance;
304+
});
305+
} catch (e) {
306+
// ignore
307+
}
308+
309+
return session;
288310
},
289311
);
290312
}

packages/backend/src/user/services/user-metadata.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import mergeWith from "lodash.mergewith";
21
import SupertokensUserMetadata, {
32
type JSONObject,
43
} from "supertokens-node/recipe/usermetadata";
4+
import { merge } from "ts-deepmerge";
55
import { Resource_Sync } from "@core/types/sync.types";
66
import {
77
type GoogleConnectionState,
@@ -126,7 +126,7 @@ class UserMetadataService {
126126
data: Partial<UserMetadata>;
127127
}): Promise<UserMetadata> => {
128128
const value = await this.getStoredUserMetadata(userId);
129-
const update = mergeWith(value, data) as UserMetadata;
129+
const update = merge(value, data) as UserMetadata;
130130

131131
const result = (await SupertokensUserMetadata.updateUserMetadata(
132132
userId,

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"eventemitter2": "^6.4.9",
1717
"rxjs": "^7.8.2",
1818
"tinycolor2": "^1.6.0",
19+
"ts-deepmerge": "^7.0.3",
1920
"winston": "^3.8.1",
2021
"zod": "^3.25.76"
2122
}

packages/core/src/__tests__/mock.setup.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { default as mockMergeWith } from "lodash.mergewith";
21
import { faker as mockFaker } from "@faker-js/faker";
32

43
export const mockBSON = () => {
@@ -32,11 +31,23 @@ export function mockModule<T>(
3231
) {
3332
const mockedModule = jest.requireActual(mockPath);
3433

35-
jest.mock<T>(mockPath, () =>
36-
mockMergeWith(
37-
{ __esModule: mockAsEsModule },
38-
mockedModule,
39-
mockFactory(mockedModule),
40-
),
41-
);
34+
jest.mock<T>(mockPath, () => {
35+
const mock = mockFactory(mockedModule);
36+
for (const key in mock) {
37+
try {
38+
(mockedModule as any)[key] = (mock as any)[key];
39+
} catch (e) {
40+
// skip read-only
41+
}
42+
}
43+
try {
44+
Object.defineProperty(mockedModule, "__esModule", {
45+
value: mockAsEsModule,
46+
configurable: true,
47+
});
48+
} catch (e) {
49+
// ignore
50+
}
51+
return mockedModule as T;
52+
});
4253
}

packages/core/src/mappers/map.event.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-namespace */
2-
import mergeWith from "lodash.mergewith";
2+
import { merge } from "ts-deepmerge";
33
import { Origin, Priorities } from "@core/constants/core.constants";
44
import { BaseError } from "@core/errors/errors.base";
55
import {
@@ -200,11 +200,10 @@ export const gEventToCompassEvent = (
200200
);
201201
}
202202

203-
const event: WithGcalId<gSchema$Event> = mergeWith(
204-
{},
203+
const event: WithGcalId<gSchema$Event> = merge(
205204
gEventDefaults,
206205
gEvent,
207-
);
206+
) as WithGcalId<gSchema$Event>;
208207

209208
const { id: gEventId, description } = event;
210209
const title = event.summary!;

0 commit comments

Comments
 (0)