Skip to content

refactor: split ThemeModeContext into multiple contexts#212

Open
Innei wants to merge 1 commit into
ant-design:masterfrom
Innei:refactor/split-theme-context
Open

refactor: split ThemeModeContext into multiple contexts#212
Innei wants to merge 1 commit into
ant-design:masterfrom
Innei:refactor/split-theme-context

Conversation

@Innei
Copy link
Copy Markdown

@Innei Innei commented Feb 9, 2026

Split ThemeModeContext into separate contexts for better state management:

  • AppearanceContext
  • ThemeModeValueContext
  • BrowserPrefersContext
  • ThemeActionContext

This improves component isolation and state management.

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增 getAntdToken() 函数,支持在不依赖 React 的情况下获取 Ant Design 设计令牌。
    • 增强 extractStaticStyle() 支持 injectTokenStyle 选项以注入 Ant Design 令牌 CSS 变量。
  • 重构

    • 重构主题上下文架构以减少不必要的重新渲染。
  • 测试

    • 新增样式创建性能基准测试。

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 9, 2026

Warning

Rate limit exceeded

@Innei has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 2 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

概览

将单一组合主题上下文拆分为四个独立原始上下文(AppearanceContext、ThemeModeValueContext、BrowserPrefersContext、ThemeActionContext),添加 getAntdToken 函数和 CSS 变量注入能力,更新组件和钩子以消费新上下文结构。

更改内容

Cohort / File(s) 摘要
上下文重构
src/context/ThemeModeContext.ts, src/context/ThemeModeContext.test.ts
将单一 ThemeModeContext 替换为四个细粒度上下文:AppearanceContext、ThemeModeValueContext、BrowserPrefersContext、ThemeActionContext;新增 ThemeActions 接口和 noop 函数。
Provider 和组件更新
src/factories/createThemeProvider/AntdProvider.tsx, src/factories/createThemeProvider/ThemeSwitcher.tsx, src/factories/createThemeProvider/ThemeSwitcher.test.tsx, src/factories/createThemeProvider/TokenContainer.tsx, src/factories/createThemeProvider/index.tsx
更新组件以消费多个新上下文而非单一 ThemeModeContext;引入 ThemeActionContext 提供者;添加 StyleEngineContext 值的记忆化。
钩子重构
src/hooks/useThemeMode.ts, src/hooks/useAntdStylish.ts, src/factories/createUseTheme.ts
替换 useThemeMode 实现以聚合多个上下文;更新 useAntdStylish 从 AppearanceContext 直接消费外观;增强 createUseTheme 集成新上下文并添加 EMPTY_THEME 常量。
Token 工具函数
src/factories/createStaticStyles/cssVar.ts, src/functions/extractStaticStyle.tsx, src/functions/index.ts
新增 getAntdToken 函数用于无需 React 获取 Ant Design token;扩展 extractStaticStyle 支持通过 injectTokenStyle 选项注入 CSS 变量;添加 getAntdToken 到公共导出。
基准和单元测试
tests/functions/createStyles.bench.tsx, tests/functions/createStyles.browser.bench.test.tsx, tests/functions/extractStaticStyle.test.tsx, tests/functions/getAntdToken.test.ts, tests/tsconfig.json
添加 createStyles 和 getAntdToken 的基准测试;添加 token CSS 变量注入的单元测试;更新 TypeScript 路径映射。

估计代码审查工作量

🎯 3 (中等) | ⏱️ ~25 分钟

🐰 四个上下文舞动新舞蹈,
Token 函数轻声吟唱,
细粒度的状态各司其职,
记忆化的提供者奏响新曲,
主题系统焕然一新!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题清晰地反映了主要变更:将单一的ThemeModeContext拆分为多个独立的上下文。标题简洁明了,准确概括了整个变更集的核心目的。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @Innei, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the theme management system by decomposing a single, broad theme context into several specialized contexts. This change aims to enhance the modularity, maintainability, and performance of theme-related state by reducing unnecessary re-renders and improving component isolation. Additionally, it introduces a new utility for accessing Ant Design tokens outside of React and extends static style extraction capabilities to better support SSR environments.

Highlights

  • Context Refactoring: The monolithic ThemeModeContext has been split into four more granular contexts: AppearanceContext, ThemeModeValueContext, BrowserPrefersContext, and ThemeActionContext. This improves component isolation and state management by allowing components to subscribe only to the specific theme aspects they need.
  • Ant Design Token Utility: A new utility function, getAntdToken, has been introduced. This function allows retrieving Ant Design token values outside of React components, which is particularly useful for non-React, SSR, or build-time scenarios.
  • SSR Style Injection Enhancement: The extractStaticStyle function now supports injecting CSS variables generated from Ant Design tokens. This feature helps prevent Flash of Unstyled Content (FOUC) during Server-Side Rendering (SSR) when using CSS variables in static styles.
  • Performance Benchmarking: New benchmark tests have been added for createStyles to measure and ensure performance, covering various styling scenarios including static objects, token-based functions, multiple objects, complex CSS templates, responsive styles, and cx merging.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/context/ThemeModeContext.test.ts
    • Updated tests to reflect the new multiple context structure, replacing ThemeModeContext with individual context imports and useContext calls.
  • src/context/ThemeModeContext.ts
    • Replaced the single ThemeModeContext with AppearanceContext, ThemeModeValueContext, BrowserPrefersContext, and ThemeActionContext.
    • Defined ThemeActions interface for theme-related setters.
  • src/factories/createStaticStyles/cssVar.ts
    • Added getAntdToken function to retrieve Ant Design tokens with optional appearance and theme configurations.
    • Modified generateCSSVarMap to utilize the new getAntdToken function.
  • src/factories/createThemeProvider/AntdProvider.tsx
    • Switched from useThemeMode to useContext(AppearanceContext) for determining appearance and isDarkMode.
  • src/factories/createThemeProvider/ThemeSwitcher.test.tsx
    • Updated tests to use the new individual theme contexts for providing values.
  • src/factories/createThemeProvider/ThemeSwitcher.tsx
    • Refactored to use the new individual theme contexts (ThemeModeValueContext, AppearanceContext, BrowserPrefersContext, ThemeActionContext) instead of a single ThemeModeContext.
  • src/factories/createThemeProvider/TokenContainer.tsx
    • Replaced useThemeMode with direct useContext calls for the new individual theme contexts to construct the theme object.
  • src/factories/createThemeProvider/index.tsx
    • Modified StyleEngineContext.Provider to use useMemo for its value, ensuring stability.
  • src/factories/createUseTheme.ts
    • Updated createUseTheme to consume the new individual theme contexts and reconstruct the Theme object.
  • src/functions/extractStaticStyle.tsx
    • Introduced InjectTokenStyleOptions and createTokenVarStyleItem to allow injecting CSS variables from Ant Design tokens into static styles.
    • Added options for filtering token keys and specifying appearance for injected styles.
  • src/functions/index.ts
    • Exported the new getAntdToken function.
  • src/hooks/useAntdStylish.ts
    • Switched from useThemeMode to useContext(AppearanceContext) for appearance and isDarkMode.
  • src/hooks/useThemeMode.ts
    • Refactored useThemeMode to aggregate values from the new individual theme contexts.
  • tests/snapshots/export.test.tsx.snap
    • Updated snapshot to include getAntdToken in the list of exported functions.
  • tests/functions/snapshots/extractStaticStyle.test.tsx.snap
    • Added new snapshots for extractStaticStyle with injectTokenStyle for dark and light modes, demonstrating CSS variable injection.
  • tests/functions/createStyles.bench.tsx
    • New file added for useStyles performance benchmarks, covering various styling patterns.
  • tests/functions/createStyles.browser.bench.test.tsx
    • New file added for browser-specific useStyles performance benchmarks, providing detailed performance metrics.
  • tests/functions/getAntdToken.test.ts
    • New file added for testing the getAntdToken function, including light/dark mode differences and theme provider-like getters.
  • tests/tsconfig.json
    • Updated paths alias for antd-style from ../es to ../src.
Activity
  • The author, Innei, initiated this pull request with a clear intent to refactor the theme mode context for improved state management.
  • The changes involve a significant restructuring of how theme-related state is managed and consumed across the application.
  • New utility functions and enhancements to static style extraction indicate a focus on both developer experience and performance, especially for SSR.
  • The addition of new benchmark tests highlights a commitment to performance optimization and ensuring the efficiency of the createStyles hook.
  • Snapshot updates confirm that the changes to exports and static style generation are correctly reflected.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a significant and well-executed refactoring that splits the monolithic ThemeModeContext into several fine-grained contexts. This is an excellent change for improving performance by reducing unnecessary re-renders. The changes are applied consistently throughout the codebase. Additionally, the introduction of getAntdToken and the injectTokenStyle feature for SSR are valuable additions that will help prevent theme flashing (FOUC). The inclusion of new tests and benchmarks is also commendable. I have a couple of suggestions to further improve performance and code clarity.

Comment thread src/hooks/useThemeMode.ts
Comment on lines +11 to +25
export const useThemeMode = (): ThemeContextState => {
const appearance = useContext(AppearanceContext);
const themeMode = useContext(ThemeModeValueContext);
const browserPrefers = useContext(BrowserPrefersContext);
const { setAppearance, setThemeMode } = useContext(ThemeActionContext);

return {
appearance,
isDarkMode: appearance === 'dark',
themeMode,
setThemeMode,
setAppearance,
browserPrefers,
};
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This hook creates a new object on every render, which can lead to unnecessary re-renders in components that consume it. To improve performance, the returned object should be memoized with useMemo. This ensures that consumers only re-render when the actual theme state values change.

Note: You'll also need to add useMemo to the import from react.

Suggested change
export const useThemeMode = (): ThemeContextState => {
const appearance = useContext(AppearanceContext);
const themeMode = useContext(ThemeModeValueContext);
const browserPrefers = useContext(BrowserPrefersContext);
const { setAppearance, setThemeMode } = useContext(ThemeActionContext);
return {
appearance,
isDarkMode: appearance === 'dark',
themeMode,
setThemeMode,
setAppearance,
browserPrefers,
};
};
export const useThemeMode = (): ThemeContextState => {
const appearance = useContext(AppearanceContext);
const themeMode = useContext(ThemeModeValueContext);
const browserPrefers = useContext(BrowserPrefersContext);
const actions = useContext(ThemeActionContext);
return useMemo(
() => ({
appearance,
isDarkMode: appearance === 'dark',
themeMode,
browserPrefers,
...actions,
}),
[appearance, themeMode, browserPrefers, actions],
);
};

Comment on lines +42 to +46
const algoProp = !themeConfig.algorithm
? []
: themeConfig.algorithm instanceof Array
? themeConfig.algorithm
: [themeConfig.algorithm];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to normalize themeConfig.algorithm into an array can be simplified. Using [].concat() can handle undefined, a single item, or an array gracefully, making the code more concise and readable.

  const algoProp = [].concat(themeConfig.algorithm || []);

…ate management

- Introduced separate contexts: AppearanceContext, ThemeModeValueContext, BrowserPrefersContext, and ThemeActionContext.
- Updated related components and hooks to utilize the new context structure.
- Refactored tests to align with the new context setup, ensuring default values and functionality remain intact.

Signed-off-by: Innei <tukon479@gmail.com>
@Innei Innei force-pushed the refactor/split-theme-context branch from a962b4b to 3c2d42d Compare February 9, 2026 17:34
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/functions/extractStaticStyle.tsx`:
- Line 147: The current line in extractStaticStyle that coerces numeric rawValue
into `${rawValue}px` causes incorrect united CSS for unitless tokens; update
extractStaticStyle to only append "px" when the token key/property requires
length units by introducing a unitless token set or a heuristic regex, e.g.,
check the token identifier (the function's key/name param) against a maintained
UNIT_LESS_TOKENS or /(lineHeight|fontWeight|opacity|zIndex)/i and if it matches
keep the numeric rawValue as-is, otherwise convert numbers to `${rawValue}px`;
modify the computation of value (which currently uses rawValue) to branch on
that check so unitless tokens like lineHeight, fontWeight, opacity, zIndex
remain unitless.

In `@tests/functions/createStyles.browser.bench.test.tsx`:
- Around line 6-14: The runBenchmark helper runs fn (which calls testing-library
render) many times without unmounting, causing DOM buildup; update runBenchmark
to call the testing-library cleanup after each iteration (e.g., call cleanup()
in a finally block after invoking fn) so each render is unmounted before the
next iteration and wrap fn invocation in try/finally to ensure cleanup always
runs; reference the runBenchmark function and the fn parameter (and tests that
call render) when making the change.

In `@tests/functions/extractStaticStyle.test.tsx`:
- Around line 76-78: The assertions for tokenStyle are in the wrong order: call
expect(tokenStyle).toBeDefined() before accessing tokenStyle.css and calling
expect(tokenStyle.css).toMatchSnapshot(); update the test in
extractStaticStyle.test.tsx to first assert the variable is defined (for
tokenStyle found via result.find(...)) and then run the snapshot assertion, and
apply the same swap for the other identical occurrences around the later
assertions (the block at the same scope as the lines flagged) so you never call
.css on a potentially undefined value.
🧹 Nitpick comments (10)
tests/functions/createStyles.browser.bench.test.tsx (1)

193-340: "性能汇总"测试与前面 6 个独立测试存在大量重复代码

benchmarks 数组中的每个 lambda 都是对应独立测试的复制粘贴。如果某个样式场景需要修改,必须同步更新两处,容易遗漏。

建议将每个场景的 createStyles + App 提取为共享工厂函数,在独立测试和汇总测试中复用。

tests/functions/createStyles.bench.tsx (1)

1-158: 基准测试结构清晰,覆盖了主要的 createStyles 使用模式

createStyles.browser.bench.test.tsx 类似,每次 bench 回调中的 render() 没有执行 cleanup()。虽然 vitest bench 的迭代管理可能减轻影响,但仍建议在每次 render() 后清理 DOM,以确保各次迭代的测量条件一致。

src/factories/createStaticStyles/cssVar.ts (1)

42-46: 建议使用 Array.isArray() 代替 instanceof Array

instanceof Array 在跨 iframe/realm 场景下可能返回 falseArray.isArray() 是更可靠的检测方式。

♻️ 建议修改
   const algoProp = !themeConfig.algorithm
     ? []
-    : themeConfig.algorithm instanceof Array
+    : Array.isArray(themeConfig.algorithm)
     ? themeConfig.algorithm
     : [themeConfig.algorithm];
tests/functions/getAntdToken.test.ts (1)

1-29: 测试覆盖了核心场景,结构清晰

建议后续补充:直接传入 ThemeConfig 对象(非函数)的测试,以及 algorithm 合并逻辑的测试。

src/context/ThemeModeContext.test.ts (1)

26-33: 测试名称与实际行为不一致

测试名 "should return false when window is undefined" 具有误导性 —— 在 JSDOM 环境中 window 是已定义的。此测试实际验证的是:BrowserPrefersContext 的默认值在模块加载时已确定为 'light',后续渲染不会再次调用 matchMedia

建议更新测试名称以准确描述行为,例如 "should use module-level default without re-calling matchMedia"

src/hooks/useThemeMode.ts (1)

11-25: 每次调用都返回新对象,考虑是否需要 memoize。

useThemeMode 每次调用都返回一个新的对象字面量。如果有消费者将整个返回值传入依赖数组或作为 prop 传递,可能导致不必要的重渲染。可以考虑用 useMemo 包裹返回值。

不过如果消费者都是解构使用,当前写法也是可接受的。

♻️ 可选优化:memoize 返回值
+import { useContext, useMemo } from 'react';
-import { useContext } from 'react';

 export const useThemeMode = (): ThemeContextState => {
   const appearance = useContext(AppearanceContext);
   const themeMode = useContext(ThemeModeValueContext);
   const browserPrefers = useContext(BrowserPrefersContext);
   const { setAppearance, setThemeMode } = useContext(ThemeActionContext);

-  return {
+  return useMemo(() => ({
     appearance,
     isDarkMode: appearance === 'dark',
     themeMode,
     setThemeMode,
     setAppearance,
     browserPrefers,
-  };
+  }), [appearance, themeMode, browserPrefers, setAppearance, setThemeMode]);
 };
src/functions/extractStaticStyle.tsx (3)

103-116: isColorLike 未覆盖现代 CSS 颜色函数格式。

当前检查遗漏了 oklch()oklab()lab()lch()hwb()color() 等现代 CSS 颜色格式。虽然目前 antd token 可能不使用这些格式,但如果用户通过自定义 theme 传入包含这些格式的值,filter: 'colors' 模式下会将其过滤掉。


118-124: isColorTokenKey 的正则匹配范围偏广。

/^[a-z]+(\d{1,2})$/i 会匹配任何 "字母+1~2位数字" 的 key(如 fontSize12lineHeight15 等非调色板 key)。虽然注释中说明了有意保持宽泛,但在 filter: 'color' 模式下可能会意外包含非颜色 token。不过在 filter: 'color' 模式下,由于还要通过 isColorTokenKeykey.startsWith('color') 分支,这里的额外匹配只影响调色板类 key 的识别,风险可控。


160-165: dangerouslySetInnerHTML 使用说明。

静态分析工具标记了此处的 dangerouslySetInnerHTML。在当前场景中,CSS 内容来源于 antd token 系统的键值对拼接,非用户直接输入,XSS 风险较低。但需注意,如果用户通过 theme 选项传入了自定义 token 值,理论上可以注入 </style> 等恶意内容来逃逸样式标签。此文件中已有的其他 dangerouslySetInnerHTML 用法(如 antd 样式和 emotion 样式)也存在相同模式,因此保持一致性是合理的。

tests/functions/extractStaticStyle.test.tsx (1)

61-98: 测试覆盖整体良好,建议补充 filter: 'all'injectTokenStyle: true(布尔值)的测试用例。

当前测试覆盖了 appearance 指定、filter: 'color' 的明暗两种模式,但缺少对 filter: 'all'(包含所有 token)和直接传 injectTokenStyle: true(布尔值,使用全部默认参数)的测试路径。这些是 createTokenVarStyleItem 的重要分支。

Comment thread src/functions/extractStaticStyle.tsx Outdated
if (rawValue === null || typeof rawValue === 'undefined') continue;

const kebabKey = toKebabCase(rawKey);
const value = typeof rawValue === 'number' ? `${rawValue}px` : String(rawValue);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

数值类型 token 统一加 px 后缀会导致无单位属性值错误。

antd 的 design token 中存在大量无单位的数值 token,例如 lineHeight(如 1.5)、fontWeightStrong(如 600)、opacityLoading(如 0.65)、zIndexPopupBase 等。将它们统一转为 ${value}px 会产生错误的 CSS 变量值(如 --ant-line-height: 1.5px),在实际使用时将导致样式异常。

建议维护一个无单位 token key 的集合,或者采用启发式方法(如检查 key 是否包含 lineHeightfontWeightzIndexopacity 等关键字)来决定是否添加 px 后缀。

🐛 建议的修复方案
+const UNITLESS_TOKEN_PATTERNS = [
+  'lineHeight', 'fontWeight', 'opacity', 'zIndex', 'motion',
+];
+
+const isUnitlessToken = (key: string): boolean =>
+  UNITLESS_TOKEN_PATTERNS.some((pattern) => key.toLowerCase().includes(pattern.toLowerCase()));
+
 // inside createTokenVarStyleItem, line 147:
-    const value = typeof rawValue === 'number' ? `${rawValue}px` : String(rawValue);
+    const value = typeof rawValue === 'number'
+      ? (isUnitlessToken(rawKey) ? String(rawValue) : `${rawValue}px`)
+      : String(rawValue);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const value = typeof rawValue === 'number' ? `${rawValue}px` : String(rawValue);
const UNITLESS_TOKEN_PATTERNS = [
'lineHeight', 'fontWeight', 'opacity', 'zIndex', 'motion',
];
const isUnitlessToken = (key: string): boolean =>
UNITLESS_TOKEN_PATTERNS.some((pattern) => key.toLowerCase().includes(pattern.toLowerCase()));
const value = typeof rawValue === 'number'
? (isUnitlessToken(rawKey) ? String(rawValue) : `${rawValue}px`)
: String(rawValue);
🤖 Prompt for AI Agents
In `@src/functions/extractStaticStyle.tsx` at line 147, The current line in
extractStaticStyle that coerces numeric rawValue into `${rawValue}px` causes
incorrect united CSS for unitless tokens; update extractStaticStyle to only
append "px" when the token key/property requires length units by introducing a
unitless token set or a heuristic regex, e.g., check the token identifier (the
function's key/name param) against a maintained UNIT_LESS_TOKENS or
/(lineHeight|fontWeight|opacity|zIndex)/i and if it matches keep the numeric
rawValue as-is, otherwise convert numbers to `${rawValue}px`; modify the
computation of value (which currently uses rawValue) to branch on that check so
unitless tokens like lineHeight, fontWeight, opacity, zIndex remain unitless.

Comment on lines +6 to +14
const runBenchmark = (name: string, fn: () => void, iterations: number = 1000) => {
const start = performance.now();
for (let i = 0; i < iterations; i++) {
fn();
}
const end = performance.now();
const avgTime = (end - start) / iterations;
return { name, avgTime, totalTime: end - start };
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

render() 在循环中缺少 cleanup,可能导致内存泄漏和基准结果失真

@testing-library/reactrender 会在 DOM 中累积挂载的组件。在 1000 次迭代中不调用 cleanup(),会导致 DOM 节点持续增长,使后续迭代越来越慢,基准结果不准确。

🔧 建议修复
+import { render, cleanup } from '@testing-library/react';
-import { render } from '@testing-library/react';
   const runBenchmark = (name: string, fn: () => void, iterations: number = 1000) => {
     const start = performance.now();
     for (let i = 0; i < iterations; i++) {
       fn();
+      cleanup();
     }
🤖 Prompt for AI Agents
In `@tests/functions/createStyles.browser.bench.test.tsx` around lines 6 - 14, The
runBenchmark helper runs fn (which calls testing-library render) many times
without unmounting, causing DOM buildup; update runBenchmark to call the
testing-library cleanup after each iteration (e.g., call cleanup() in a finally
block after invoking fn) so each render is unmounted before the next iteration
and wrap fn invocation in try/finally to ensure cleanup always runs; reference
the runBenchmark function and the fn parameter (and tests that call render) when
making the change.

Comment on lines +76 to +78
const tokenStyle = result.find((i) => i.key === 'ant-token-dark')!;
expect(tokenStyle.css).toMatchSnapshot();
expect(tokenStyle).toBeDefined();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

断言顺序应调整:先验证 toBeDefined() 再做快照匹配。

当前代码在第 77 行先调用 toMatchSnapshot(),然后在第 78 行才断言 toBeDefined()。如果 tokenStyleundefined,快照会静默匹配 undefined 而不会报错,导致测试通过但实际未验证任何有意义的内容。第 92-94 行存在相同问题。

🔧 建议的修复
      const tokenStyle = result.find((i) => i.key === 'ant-token-dark')!;
-     expect(tokenStyle.css).toMatchSnapshot();
      expect(tokenStyle).toBeDefined();
+     expect(tokenStyle.css).toMatchSnapshot();
      expect(tokenStyle.css).toMatch(/--ant-color-bg-base:/);

第 92-94 行同理:

      const tokenStyle = result.find((i) => i.key === 'ant-token-light')!;
-     expect(tokenStyle.css).toMatchSnapshot();
      expect(tokenStyle).toBeDefined();
+     expect(tokenStyle.css).toMatchSnapshot();
      expect(tokenStyle.css).toMatch(/--ant-color-bg-base:/);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tokenStyle = result.find((i) => i.key === 'ant-token-dark')!;
expect(tokenStyle.css).toMatchSnapshot();
expect(tokenStyle).toBeDefined();
const tokenStyle = result.find((i) => i.key === 'ant-token-dark')!;
expect(tokenStyle).toBeDefined();
expect(tokenStyle.css).toMatchSnapshot();
expect(tokenStyle.css).toMatch(/--ant-color-bg-base:/);
🤖 Prompt for AI Agents
In `@tests/functions/extractStaticStyle.test.tsx` around lines 76 - 78, The
assertions for tokenStyle are in the wrong order: call
expect(tokenStyle).toBeDefined() before accessing tokenStyle.css and calling
expect(tokenStyle.css).toMatchSnapshot(); update the test in
extractStaticStyle.test.tsx to first assert the variable is defined (for
tokenStyle found via result.find(...)) and then run the snapshot assertion, and
apply the same swap for the other identical occurrences around the later
assertions (the block at the same scope as the lines flagged) so you never call
.css on a potentially undefined value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant