Skip to content

Commit ff31f72

Browse files
authored
feat(clerk-js,shared): Custom logger to enable logging once per session (#3383)
1 parent 7fca4a5 commit ff31f72

File tree

9 files changed

+76
-6
lines changed

9 files changed

+76
-6
lines changed

.changeset/clean-kings-brake.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
'@clerk/shared': patch
4+
---
5+
6+
Add a custom logger to allow logging a message or warning to the console once per session, in order to avoid consecutive identical logs due to component rerenders.

packages/clerk-js/src/utils/__tests__/url.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { logger } from '@clerk/shared/logger';
12
import type { SignUpResource } from '@clerk/types';
23

34
import {
@@ -473,7 +474,7 @@ describe('isAllowedRedirectOrigin', () => {
473474
['https://test.clerk.com/foo?hello=1', [/https:\/\/www\.clerk\.com/], false],
474475
];
475476

476-
const warnMock = jest.spyOn(global.console, 'warn').mockImplementation();
477+
const warnMock = jest.spyOn(logger, 'warnOnce');
477478

478479
beforeEach(() => warnMock.mockClear());
479480
afterAll(() => warnMock.mockRestore());

packages/clerk-js/src/utils/assertNoLegacyProp.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { logger } from '@clerk/shared/logger';
2+
13
export function assertNoLegacyProp(props: Record<string, any>) {
24
const legacyProps = ['redirectUrl', 'afterSignInUrl', 'afterSignUpUrl', 'after_sign_in_url', 'after_sign_up_url'];
35
const legacyProp = Object.keys(props).find(key => legacyProps.includes(key));
46

57
if (legacyProp && props[legacyProp]) {
68
// TODO: @nikos update with the docs link
7-
console.warn(
9+
logger.warnOnce(
810
`Clerk: The prop "${legacyProp}" is deprecated and should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead.`,
911
);
1012
}
@@ -18,7 +20,7 @@ export function warnForNewPropShadowingLegacyProp(
1820
) {
1921
if (newValue && legacyValue) {
2022
// TODO: @nikos update with the docs link
21-
console.warn(
23+
logger.warnOnce(
2224
`Clerk: The "${newKey}" prop ("${newValue}") has priority over the legacy "${legacyKey}" (or "redirectUrl") ("${legacyValue}"), which will be completely ignored in this case. "${legacyKey}" (or "redirectUrl" prop) should be replaced with the new "fallbackRedirectUrl" or "forceRedirectUrl" props instead.`,
2325
);
2426
}

packages/clerk-js/src/utils/url.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { globs } from '@clerk/shared/globs';
22
import { createDevOrStagingUrlCache } from '@clerk/shared/keys';
3+
import { logger } from '@clerk/shared/logger';
34
import { camelToSnake } from '@clerk/shared/underscore';
45
import { isCurrentDevAccountPortalOrigin, isLegacyDevAccountPortalOrigin } from '@clerk/shared/url';
56
import type { SignUpResource } from '@clerk/types';
@@ -116,7 +117,7 @@ export function buildURL(params: BuildURLParams, options: BuildURLOptions<boolea
116117
// Merge search params from hashSearch string
117118
const searchParamsFromHashSearchString = getQueryParams(hashSearch || '');
118119
for (const [key, val] of Object.entries(searchParamsFromHashSearchString)) {
119-
dummyUrlForHash.searchParams.append(key, val as string);
120+
dummyUrlForHash.searchParams.append(key, val);
120121
}
121122

122123
// Merge search params from the hashSearchParams object
@@ -354,7 +355,7 @@ export const isAllowedRedirectOrigin =
354355
.some(origin => origin.test(trimTrailingSlash(url.origin)));
355356

356357
if (!isAllowed) {
357-
console.warn(
358+
logger.warnOnce(
358359
`Clerk: Redirect URL ${url} is not on one of the allowedRedirectOrigins, falling back to the default redirect URL.`,
359360
);
360361
}

packages/shared/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
"constants",
6969
"apiUrlFromPublishableKey",
7070
"scripts",
71-
"telemetry"
71+
"telemetry",
72+
"logger"
7273
],
7374
"scripts": {
7475
"build": "tsup",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { logger } from '../logger';
2+
3+
describe('logger', () => {
4+
describe('warnOnce', () => {
5+
const warnMock = jest.spyOn(global.console, 'warn').mockImplementation();
6+
7+
beforeEach(() => warnMock.mockClear());
8+
afterAll(() => warnMock.mockRestore());
9+
10+
test('warns only once per session', () => {
11+
logger.warnOnce('testwarn');
12+
logger.warnOnce('testwarn');
13+
logger.warnOnce('testwarn');
14+
15+
expect(warnMock).toHaveBeenCalledTimes(1);
16+
});
17+
});
18+
19+
describe('logOnce', () => {
20+
const logMock = jest.spyOn(global.console, 'log').mockImplementation();
21+
22+
beforeEach(() => logMock.mockClear());
23+
afterAll(() => logMock.mockRestore());
24+
25+
test('logs only once per session', () => {
26+
logger.logOnce('testlog');
27+
logger.logOnce('testlog');
28+
logger.logOnce('testlog');
29+
30+
expect(logMock).toHaveBeenCalledTimes(1);
31+
});
32+
});
33+
});

packages/shared/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ export * from './proxy';
3030
export * from './underscore';
3131
export * from './url';
3232
export * from './object';
33+
export * from './logger';
3334
export { createWorkerTimers } from './workerTimers';
3435
export { DEV_BROWSER_JWT_KEY, extractDevBrowserJWTFromURL, setDevBrowserJWTInURL } from './devBrowser';

packages/shared/src/logger.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const loggedMessages: Set<string> = new Set();
2+
3+
export const logger = {
4+
/**
5+
* A custom logger that ensures messages are logged only once.
6+
* Reduces noise and duplicated messages when logs are in a hot codepath.
7+
*/
8+
warnOnce: (msg: string) => {
9+
if (loggedMessages.has(msg)) {
10+
return;
11+
}
12+
13+
loggedMessages.add(msg);
14+
console.warn(msg);
15+
},
16+
logOnce: (msg: string) => {
17+
if (loggedMessages.has(msg)) {
18+
return;
19+
}
20+
21+
console.log(msg);
22+
loggedMessages.add(msg);
23+
},
24+
};

packages/shared/subpaths.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const subpathNames = [
2424
'constants',
2525
'apiUrlFromPublishableKey',
2626
'telemetry',
27+
'logger',
2728
];
2829

2930
export const subpathFoldersBarrel = ['react'];

0 commit comments

Comments
 (0)