Skip to content

Commit 1bc025d

Browse files
sungik-choicursoragented
authored
fix(warn): Optimize console warnings to prevent excessive output in development (#2661)
<!-- How to write a good PR title: - Follow [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). - Give as much context as necessary and as little as possible - Prefix it with [WIP] while it’s a work in progress --> ## Self Checklist - [x] I wrote a PR title in **English** and added an appropriate **label** to the PR. - [x] I wrote the commit message in **English** and to follow [**the Conventional Commits specification**](https://www.conventionalcommits.org/en/v1.0.0/). - [x] I [added the **changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md) about the changes that needed to be released. (or didn't have to) - [ ] I wrote or updated **documentation** related to the changes. (or didn't have to) - [x] I wrote or updated **tests** related to the changes. (or didn't have to) - [ ] I tested the changes in various browsers. (or didn't have to) - Windows: Chrome, Edge, (Optional) Firefox - macOS: Chrome, Edge, Safari, (Optional) Firefox ## Related Issue <!-- Please link to issue if one exists --> - Fixes #2644 ## Summary Optimizes the `warn` utility function to prevent excessive console warnings in development mode, specifically addressing performance issues when many components trigger the same warning (e.g., legacy icon deprecation). ## Details This PR introduces a `scope` parameter to the `warn` function. When a `scope` is provided, the warning message for that specific scope will only be logged to the console once per development session. This change was made to: - Prevent UI blocking and developer tool freezing caused by hundreds or thousands of identical console warnings, especially when using components with deprecated features (like legacy icons) in large lists. - Improve the developer experience by reducing console noise while still providing necessary warnings. **Key Changes:** - **`packages/bezier-react/src/utils/assert.ts`**: - `warn` function overloaded to accept an optional `scope` string. - Uses an internal `Set` (`devWarningScopes`) to track and ensure each scoped warning is logged only once. - **Component Updates**: - Applied the new `scope` parameter to existing legacy icon deprecation warnings in `Button`, `Banner`, and `SectionLabel` components. - **Test Coverage**: - Added new tests for the `warn` function to verify that warnings are logged only once per scope and that different scopes are handled independently. ### Breaking change? (Yes/No) No. This is an internal utility improvement that only affects development-mode console output. ## References - Inspired by `react-window`'s warning mechanism: - https://github.com/bvaughn/react-window/blob/72db696dd8ebb7f0f287c78d037ff68ba9534183/src/createListComponent.js#L674-L682 - https://github.com/bvaughn/react-window/blob/72db696dd8ebb7f0f287c78d037ff68ba9534183/src/__tests__/FixedSizeList.js#L868-L885 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **신규 기능** * 경고 메시지에 scope(범위) 파라미터가 추가되어, 동일 범위 내에서는 경고가 한 번만 표시됩니다. * **버그 수정** * Button, Banner, SectionLabel 컴포넌트의 레거시 아이콘 사용 시 과도한 경고로 인한 UI 지연 현상이 개선되었습니다. * **테스트** * scope별 경고 동작을 검증하는 테스트가 추가되었습니다. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: ed <[email protected]>
1 parent 1f0c5a1 commit 1bc025d

File tree

6 files changed

+76
-7
lines changed

6 files changed

+76
-7
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@channel.io/bezier-react": patch
3+
---
4+
5+
Optimize warn function to prevent duplicate console logs
6+
7+
- Add `scope` parameter to `warn` so each message logs only once per scope
8+
- Apply the scoped warning to legacy-icon deprecation in Button, Banner, SectionLabel
9+
- Add comprehensive tests for the new `warn` behavior
10+
- Prevent UI blocking and developer tool freezing when using legacy icons with virtual lists

packages/bezier-react/src/components/Banner/Banner.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ export const Banner = forwardRef<HTMLDivElement, BannerProps>(function Banner(
7777
) {
7878
if (isIconName(icon)) {
7979
warn(
80-
'Deprecation: IconName as a value for the icon property of Banner has been deprecated. Use the Icon of bezier-icons instead.'
80+
'Deprecation: IconName as a value for the icon property of Banner has been deprecated. Use the Icon of bezier-icons instead.',
81+
'Banner.IconName'
8182
)
8283
}
8384

packages/bezier-react/src/components/Button/Button.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ function ButtonSideContent({
6464
}) {
6565
if (isIconName(children)) {
6666
warn(
67-
'Deprecation: IconName as a value for the leftContent property of a Button has been deprecated. Use the Icon of bezier-icons instead.'
67+
'Deprecation: IconName as a value for the leftContent property of a Button has been deprecated. Use the Icon of bezier-icons instead.',
68+
'Button.IconName'
6869
)
6970
return (
7071
<LegacyIcon

packages/bezier-react/src/components/SectionLabel/SectionLabel.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ function LeftContent({ children }: { children: SectionLabelLeftContent }) {
4040

4141
if (isLegacyIcon) {
4242
warn(
43-
'Deprecation: IconName as a value for the icon property of SectionLabel has been deprecated. Use the Icon of bezier-icons instead.'
43+
'Deprecation: IconName as a value for the icon property of SectionLabel has been deprecated. Use the Icon of bezier-icons instead.',
44+
'SectionLabel.LeftContent.IconName'
4445
)
4546
}
4647

@@ -76,7 +77,8 @@ function RightContent({ children }: { children: SectionLabelRightContent }) {
7677

7778
if (isLegacyIcon) {
7879
warn(
79-
'Deprecation: IconName as a value for the icon property of SectionLabel has been deprecated. Use the Icon of bezier-icons instead.'
80+
'Deprecation: IconName as a value for the icon property of SectionLabel has been deprecated. Use the Icon of bezier-icons instead.',
81+
'SectionLabel.RightContent.IconName'
8082
)
8183
}
8284

packages/bezier-react/src/utils/assert.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,47 @@ describe('warn', () => {
8686

8787
warnSpy.mockRestore()
8888
})
89+
90+
it('should output the message only once per scope when scope is provided', () => {
91+
process.env.NODE_ENV = 'development'
92+
93+
const warnSpy = jest.spyOn(console, 'warn')
94+
95+
warn('Warning message', 'test-scope')
96+
warn('Warning message', 'test-scope')
97+
warn('Warning message', 'test-scope')
98+
99+
expect(warnSpy).toHaveBeenCalledTimes(1)
100+
expect(warnSpy).toHaveBeenCalledWith('Warning message')
101+
102+
warnSpy.mockRestore()
103+
})
104+
105+
it('should output different messages for different scopes', () => {
106+
process.env.NODE_ENV = 'development'
107+
108+
const warnSpy = jest.spyOn(console, 'warn')
109+
110+
warn('Warning message 1', 'scope-1')
111+
warn('Warning message 2', 'scope-2')
112+
warn('Warning message 1', 'scope-1') // Should not be called again
113+
114+
expect(warnSpy).toHaveBeenCalledTimes(2)
115+
expect(warnSpy).toHaveBeenNthCalledWith(1, 'Warning message 1')
116+
expect(warnSpy).toHaveBeenNthCalledWith(2, 'Warning message 2')
117+
118+
warnSpy.mockRestore()
119+
})
120+
121+
it('should not output warning in production environment even with scope', () => {
122+
process.env.NODE_ENV = 'production'
123+
124+
const warnSpy = jest.spyOn(console, 'warn')
125+
126+
warn('Warning message', 'test-scope')
127+
128+
expect(warnSpy).not.toHaveBeenCalled()
129+
130+
warnSpy.mockRestore()
131+
})
89132
})

packages/bezier-react/src/utils/assert.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@ export function isDev() {
22
return process.env.NODE_ENV !== 'production'
33
}
44

5-
export function warn(message: string) {
5+
const devWarningScopes = new Set<string>()
6+
7+
export function warn(message: string): void
8+
export function warn(message: string, scope: string): void
9+
export function warn(message: string, scope?: string) {
610
if (isDev()) {
7-
// eslint-disable-next-line no-console
8-
console.warn(message)
11+
if (scope) {
12+
if (!devWarningScopes.has(scope)) {
13+
devWarningScopes.add(scope)
14+
// eslint-disable-next-line no-console
15+
console.warn(message)
16+
}
17+
} else {
18+
// eslint-disable-next-line no-console
19+
console.warn(message)
20+
}
921
}
1022
}
1123

0 commit comments

Comments
 (0)