Skip to content

Conversation

@MadCcc
Copy link
Member

@MadCcc MadCcc commented Nov 17, 2025

Summary by CodeRabbit

新功能

  • 新增自动前缀(AutoPrefix)转换器,支持自动处理浏览器厂商前缀
  • 增强CSS变量功能,支持哈希优先级和更灵活的配置选项
  • 新增DesignTokenProvider组件,简化主题提供者的使用

Bug修复

  • 改进内容引号检查器,现支持CSS var() 函数

文档

  • 新增First Render性能测试示例

重构

  • 移除autoClear演示和相关功能
  • 简化样式注册机制,优化SSR性能
  • 改进样式缓存策略,增强更新追踪

Chores

  • 版本升级至 2.0.0-alpha.9
  • 更新开发依赖,包括ESLint、Testing Library等

MadCcc and others added 26 commits February 5, 2025 12:09
* feat: rm style tokenKey

* test: fix test cases

* chore: fix tsc

* feat: remove wrapSSR

* docs: update examples
* feat: css var support hash

* chore: remove useless comment

* chore: fix tsc

* test: add test case

* chore: rm only
* chore: record updateTime

* test: add test case
* fix: content

* fix: content

* feat: 更新文档

* feat: 更新文档

* feat: 更新测试用例

* feat: 更新测试用例
@github-actions
Copy link

github-actions bot commented Nov 17, 2025

🎊 PR Preview a4da2e9 has been successfully built and deployed to https://ant-design-cssinjs-preview-pr-236.surge.sh

🕐 Build time: 88.008s

🤖 By surge-preview

@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

概览

本 PR 引入了自动前缀转换器功能、重构了令牌缓存和样式注册系统以支持 CSS 变量和哈希机制、移除了 React 兼容性垫片,并将版本升级至 2.0.0-alpha.9。此外还更新了文档示例和相应的测试。

变更

内容分组 / 文件 变更摘要
文档示例
docs/demo/auto-clear.md, docs/examples/auto-clear.tsx
删除 Auto Clear 演示文档和示例代码
文档示例
docs/demo/first-render.md, docs/examples/first-render.tsx
添加 First Render 演示文档和包含性能测量的示例组件
文档示例
docs/examples/autoPrefix.tsx
新增展示自动前缀处理的示例文件
核心样式系统
src/transformers/autoPrefix.ts, src/index.ts
引入新的 AUTO_PREFIX 转换器和 autoPrefixTransformer 导出
上下文与提供者
src/StyleContext.tsx
移除 autoClear 属性,添加 autoPrefix 属性;更新 StyleProviderProps 签名
令牌缓存
src/hooks/useCacheToken.tsx
大幅重构令牌缓存逻辑:cssVar 必需,添加哈希优先级支持,简化 TokenCacheValue 类型,调整缓存策略
CSS 变量注册
src/hooks/useCSSVarRegister.ts
添加可选的 hashId 参数并传递 hashPriority
样式注册
src/hooks/useStyleRegister.tsx
添加 autoPrefix 参数到 normalizeStyle,简化 StyleCacheValue 类型,移除 ATTR_TOKEN 导出
全局缓存
src/hooks/useGlobalCache.tsx
使用 useInsertionEffect 替代兼容性垫片,新增 onCacheEffect 回调,添加 effectMap 全局跟踪
清理注册
src/hooks/useEffectCleanupRegister.ts
重命名为 useEffectCleanupRegister,移除条件化插入效果逻辑,始终使用 useEffect
移除兼容性
src/hooks/useCompatibleInsertionEffect.tsx
删除整个文件(React useInsertionEffect 兼容性垫片)
主题与令牌
src/examples/components/theme.tsx
新增 DesignTokenProvider 组件,扩展 useToken 返回元组包含 actualToken,调整缓存流程
按钮组件
src/examples/components/Button.tsx
引入 ButtonToken 接口,添加 useCSSVarRegister 调用,重新组织令牌处理流程
转轴组件
src/examples/components/Spin.tsx
移除 useStyleRegister 返回值的包装,直接返回 div 元素
CSS 变量示例
src/examples/css-var.tsx
使用 DesignTokenProvider 替代上下文提供者,添加 Demo 子组件,集成主题切换逻辑
缓存系统
src/Cache.ts
添加 updateId 计数器和 updateTimes 映射以追踪键级更新时间
样式提取
src/extractStyle.ts
映射步骤现在生成三元组 [order, style, updateTime],更新排序逻辑以支持 updateTime 辅助排序
CSS 变量工具
src/util/css-variables.ts
为 serializeCSSVar 和 transformToken 添加 hashCls 及 hashPriority 选项
通用工具
src/util/index.ts
新增 where 工具函数,根据 hashPriority 生成 CSS 选择器
内容检查器
src/linters/contentQuotesLinter.ts
为 content 属性添加 var() 函数值的允许规则
项目配置
package.json
版本升级至 2.0.0-alpha.9,添加 tsc 脚本,更新发布脚本,升级多个 devDependencies
单元测试
tests/animation.spec.tsx, tests/keyframes.spec.tsx, tests/linter.spec.tsx, tests/theme.spec.tsx, tests/util.spec.tsx
所有 useCacheToken 调用添加 cssVar 选项对象,更新样式计数预期和断言
新增测试
tests/autoPrefix.spec.tsx
新增自动前缀转换器功能测试
删除测试
tests/legacy.spec.tsx
移除旧版 React 兼容性测试
CSS 变量测试
tests/css-variables.spec.tsx
更新提供者 props(cssVar 必需),修改默认上下文初始化,移除 autoClear 用法
索引测试
tests/index.spec.tsx
所有 useCacheToken 调用添加 cssVar 选项,调整样式生成预期以适应 CSS 变量块
服务端测试
tests/server.spec.tsx
添加 cssVar 选项,简化 useStyleRegister 用法,使用快照断言,更新样式哈希处理

序列图

sequenceDiagram
    participant App as 应用组件
    participant Provider as StyleProvider<br/>(含 autoPrefix)
    participant Context as StyleContext
    participant Register as useStyleRegister
    participant Normalizer as normalizeStyle<br/>(autoPrefix=true)
    participant Stylis as stylis<br/>前缀中间件
    
    App->>Provider: 渲染,配置<br/>transformers含 AUTO_PREFIX
    Provider->>Context: 设置 autoPrefix=true
    App->>Register: 注册样式
    Register->>Normalizer: 调用 normalizeStyle(css, true)
    alt autoPrefix 启用
        Normalizer->>Stylis: 应用前缀中间件
        Stylis->>Normalizer: 返回前缀化的 CSS
    else autoPrefix 禁用
        Normalizer->>Normalizer: 直接序列化
    end
    Normalizer->>Register: 返回规范化后的样式字符串
    Register->>Context: 注入带前缀的样式到 DOM
Loading
sequenceDiagram
    participant Component as 组件
    participant useToken as useToken()
    participant useCacheToken as useCacheToken()
    participant TransformToken as transformToken<br/>(hashCls, hashPriority)
    participant SerializeVar as serializeCSSVar<br/>(含 where 选择器)
    participant Cache as 缓存存储
    
    Component->>useToken: 获取令牌
    useToken->>useCacheToken: 调用(需要 cssVar 配置)
    useCacheToken->>TransformToken: 传递 hashPriority 和 hashCls
    TransformToken->>SerializeVar: 生成 CSS 变量
    alt hashPriority='low'
        SerializeVar->>SerializeVar: 使用 :where(.hash-cls) 选择器
    else hashPriority 非 'low'
        SerializeVar->>SerializeVar: 使用 .hash-cls 选择器
    end
    SerializeVar->>Cache: 存储 CSS 变量字符串
    useCacheToken->>Component: 返回 [token, hashCls, actualToken]
    Component->>Component: 应用样式和 CSS 变量
Loading

代码审查工作量估计

🎯 4 (复杂) | ⏱️ ~60 分钟

需要重点关注的文件:

  • src/hooks/useCacheToken.tsx — 核心令牌缓存逻辑的大幅重构,涉及 cssVar 必需化、哈希优先级引入、缓存流程改造,需要深入理解新的令牌派生和 CSS 变量注入机制
  • src/hooks/useStyleRegister.tsx — 样式注册和缓存值类型的重大改变,涉及 autoPrefix 参数传递、tokenKey 移除、StyleCacheValue 元组结构简化,需要验证样式生成和 SSR 兼容性
  • src/StyleContext.tsx — 公共接口变更(autoClear 移除,autoPrefix 添加),需要检查 autoPrefix 传播逻辑和转换器集成
  • src/hooks/useGlobalCache.tsx — useInsertionEffect 替换、effectMap 全局跟踪、新增 onCacheEffect 回调机制,需要验证生命周期正确性和缓存清理行为
  • 测试套件 (tests/) — 大量测试断言变更反映了行为重构,需要逐一验证新 cssVar 选项对样式计数、哈希处理和样式顺序的影响

可能相关的 PR

  • ant-design/cssinjs#230 — 实现相同的 autoPrefix 功能,包括转换器模块、StyleContext 集成、测试覆盖,代码级直接相关

🐰 样式系统焕然一新
自动前缀护英姿,哈希优先显身手
缓存清理更精妙,CSS 变量绽芬芳
兼容垫片已远去,新 React 年代昂然立
— 一只欢快的编码兔

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive 标题'chore: master merge next'过于宽泛,未能清晰传达本PR的核心变更内容。虽然描述提到版本升级到2.0.0-alpha.9和多项功能特性,但标题仅表示'主分支合并next分支',缺乏具体信息。 建议使用更具体的标题反映主要变更,如'chore: release 2.0.0-alpha.9 with autoPrefix and CSS variable improvements'或类似的描述性标题。
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/merge-next

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

Summary of Changes

Hello @MadCcc, 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 introduces significant improvements to the CSS-in-JS library, focusing on modernizing its CSS variable handling, enhancing browser compatibility with automatic vendor prefixing, and boosting performance through optimized effect caching and SSR rendering. The changes also reflect a commitment to React 18+ by removing legacy compatibility code and updating core dependencies.

Highlights

  • CSS Variable Enhancements: Improved CSS variable support with hashing and dynamic key generation, making them more robust and flexible. This includes updates to useCSSVarRegister and useCacheToken to handle hashId and hashPriority for CSS variables, and a fix for content verification when using CSS variables.
  • Automatic Vendor Prefixing: Introduced autoPrefixTransformer and an autoPrefix option within StyleProvider to automatically add vendor prefixes to CSS properties, enhancing cross-browser compatibility without manual intervention.
  • SSR Rendering Order Fix: Implemented a fix to ensure consistent rendering order in Server-Side Rendering (SSR) by tracking update times for cached styles and sorting them accordingly during extraction.
  • Performance Optimizations: Enhanced performance through effect caching in useGlobalCache using an effectMap and microtask-based cleanup for single-pass effect execution. The extractStyle function now also supports a once option to prevent redundant style re-extraction.
  • React 18+ Focus: Removed legacy React compatibility code (useCompatibleInsertionEffect and legacy.spec.tsx), indicating a shift to directly leverage React 18+ features like useInsertionEffect.
  • Documentation & Examples: Added new demonstration pages for 'first render' performance and 'auto-prefixing', while removing the 'auto-clear' demo.
  • Dependency Updates: Bumped various development dependencies, including @rc-component/father-plugin, @testing-library/react, and eslint, and updated the package version to 2.0.0-alpha.9.
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

@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 introduces a significant refactoring for version 2.0, dropping support for React versions below 18 and greatly simplifying the effect and cleanup logic by adopting useInsertionEffect. The changes bring substantial improvements to CSS variable support, SSR style extraction order, and introduce an autoPrefix transformer. The API is also cleaner with the removal of the wrapSSR HOC pattern. Overall, these are excellent changes that modernize the library's core. I've added a few comments regarding type safety, the use of global mutable state, and code clarity that could further enhance the codebase.

}),
);

const mergedToken: any = {

Choose a reason for hiding this comment

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

medium

Using any for mergedToken weakens type safety. A more specific type can be used here to ensure the mergedToken object conforms to the expected shape, which appears to be ButtonToken.

Suggested change
const mergedToken: any = {
const mergedToken: ButtonToken = {

}

/** Record update id for extract static style order. */
let updateId = 0;

Choose a reason for hiding this comment

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

medium

The introduction of a global, mutable updateId could pose a risk in concurrent server-side rendering (SSR) environments. If a single server process handles multiple requests concurrently and the cache instance is shared, this could lead to race conditions and incorrect style ordering. If the design ensures that a new cache is created for each SSR request, it would be beneficial to add a comment here clarifying that this global variable is safe under the expected usage patterns.

}

const TOKEN_THRESHOLD = 0;
const TOKEN_THRESHOLD = -1;

Choose a reason for hiding this comment

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

medium

Changing TOKEN_THRESHOLD from 0 to -1 effectively disables the token cache cleanup based on its usage count. While this likely resolves issues with premature style removal, especially in React 18's Strict Mode, the reason for this change is not immediately obvious. Adding a comment to explain why this threshold is set to -1 would be very helpful for future maintenance and understanding the caching strategy.

},
) => [order: number, styleId: string, style: string] | null;

const effectMap = new Map<string, boolean>();

Choose a reason for hiding this comment

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

medium

The use of a global effectMap introduces a global mutable state. While the microtask-based cleanup is a clever way to coalesce effects within a single render, this pattern can be risky, particularly in concurrent environments like SSR if not managed on a per-request basis. It would be beneficial to add a comment explaining the scope of this map and why it's safe in the intended execution environments.

expect(styles).toHaveLength(1);

const styleText = styles[0].innerHTML;
console.log('AutoPrefix test output:', styleText);

Choose a reason for hiding this comment

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

medium

This console.log statement appears to be for debugging purposes and should be removed from the final test code to keep the test output clean.

@socket-security
Copy link

socket-security bot commented Nov 17, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​rc-component/​np@​1.0.4721006985100
Updated@​rc-component/​father-plugin@​1.1.0 ⏵ 2.1.379 -110079 +288 +1100
Updated@​umijs/​fabric@​2.14.1 ⏵ 4.0.19710087 +184100
Updated@​testing-library/​react@​13.4.0 ⏵ 14.3.1100 +110010088100

View full report

@codecov
Copy link

codecov bot commented Nov 17, 2025

Codecov Report

❌ Patch coverage is 97.44526% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.63%. Comparing base (e7fd8a3) to head (a4da2e9).
⚠️ Report is 28 commits behind head on master.

Files with missing lines Patch % Lines
src/hooks/useStyleRegister.tsx 93.45% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #236      +/-   ##
==========================================
- Coverage   94.89%   94.63%   -0.26%     
==========================================
  Files          33       32       -1     
  Lines        2899     2815      -84     
  Branches      459      445      -14     
==========================================
- Hits         2751     2664      -87     
- Misses        148      151       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
tests/index.spec.tsx (2)

445-451: 修复缺失的 cssVar 配置导致的编译错误。

Line 449 的 useCacheToken 调用缺少必需的 cssVar 属性,导致 TypeScript 编译错误 TS2345。根据新的 API 签名,cssVar 选项现在是必需的。

应用此差异以修复:

     const CssVarBox = () => {
       const [token] = useCacheToken<DerivativeToken>(
         theme,
         [{ primaryColor: 'red' }],
         {
           salt: 'test',
+          cssVar: { key: 'css-var-test' },
         },
       );

468-473: 移除不存在的 autoClear 属性导致的编译错误。

Line 469 的 StyleProvider 使用了 autoClear 属性,但该属性在新的 API 中已被移除,导致 TypeScript 编译错误 TS2322。根据 src/StyleContext.tsx 中的更新,StyleProviderProps 不再包含 autoClear

应用此差异以修复:

     const { unmount } = render(
-      <StyleProvider cache={createCache()} container={container} autoClear>
+      <StyleProvider cache={createCache()} container={container}>
         <Box />
         <CssVarBox />
       </StyleProvider>
src/hooks/useStyleRegister.tsx (1)

405-520: 缓存键需包含 autoPrefix 状态

normalizeStyle 现在会根据 autoPrefix 生成不同的样式串,但 useGlobalCache 的 key 仍然只由 fullPath 控制;该路径不包含 autoPrefix 信息。一旦组件在 autoPrefix=false 时写入缓存,即使上层 StyleContext 切换为 autoPrefix=true,这里仍会复用旧缓存,不会重新生成带厂商前缀的样式,导致自动前缀功能失效。建议把 autoPrefix 的取值纳入 fullPath/cachePath,确保不同配置拥有各自的缓存。

-  const fullPath: string[] = [hashId || ''];
+  const fullPath: string[] = [hashId || ''];
   if (enableLayer) {
     fullPath.push('layer');
   }
+  if (autoPrefix) {
+    fullPath.push('auto-prefix');
+  }
   fullPath.push(...path);
🧹 Nitpick comments (1)
docs/examples/first-render.tsx (1)

16-25: 考虑渲染组件数量是否过多。

演示渲染了 10,000 个组件集合(共 40,000 个按钮),这可能会导致:

  • 浏览器性能问题
  • 测试运行缓慢
  • 用户体验不佳

如果这是为了性能基准测试,建议:

  1. 添加注释说明为什么需要这么多组件
  2. 考虑将数量设为可配置的
+const RENDER_COUNT = 10000; // Large number for performance testing
+
 const Demo = () => {
   const renderStart = React.useRef(Date.now());
   const [renderTime, setRenderTime] = React.useState(0);
 
   React.useEffect(() => {
     setRenderTime(Date.now() - renderStart.current);
   }, []);
 
   return (
     <>
       <p>Render Time: {renderTime}ms</p>
-      {Array(10000)
+      {Array(RENDER_COUNT)
         .fill(1)
         .map((_, key) => (
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7fd8a3 and 68e4fed.

⛔ Files ignored due to path filters (3)
  • tests/__snapshots__/css-variables.spec.tsx.snap is excluded by !**/*.snap
  • tests/__snapshots__/index.spec.tsx.snap is excluded by !**/*.snap
  • tests/__snapshots__/server.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (34)
  • docs/demo/auto-clear.md (0 hunks)
  • docs/demo/first-render.md (1 hunks)
  • docs/examples/auto-clear.tsx (0 hunks)
  • docs/examples/autoPrefix.tsx (1 hunks)
  • docs/examples/components/Button.tsx (7 hunks)
  • docs/examples/components/Spin.tsx (1 hunks)
  • docs/examples/components/theme.tsx (2 hunks)
  • docs/examples/css-var.tsx (2 hunks)
  • docs/examples/first-render.tsx (1 hunks)
  • package.json (3 hunks)
  • src/Cache.ts (3 hunks)
  • src/StyleContext.tsx (3 hunks)
  • src/extractStyle.ts (3 hunks)
  • src/hooks/useCSSVarRegister.ts (2 hunks)
  • src/hooks/useCacheToken.tsx (6 hunks)
  • src/hooks/useCompatibleInsertionEffect.tsx (0 hunks)
  • src/hooks/useEffectCleanupRegister.ts (2 hunks)
  • src/hooks/useGlobalCache.tsx (2 hunks)
  • src/hooks/useStyleRegister.tsx (6 hunks)
  • src/index.ts (2 hunks)
  • src/linters/contentQuotesLinter.ts (1 hunks)
  • src/transformers/autoPrefix.ts (1 hunks)
  • src/util/css-variables.ts (3 hunks)
  • src/util/index.ts (2 hunks)
  • tests/animation.spec.tsx (11 hunks)
  • tests/autoPrefix.spec.tsx (1 hunks)
  • tests/css-variables.spec.tsx (7 hunks)
  • tests/index.spec.tsx (32 hunks)
  • tests/keyframes.spec.tsx (1 hunks)
  • tests/legacy.spec.tsx (0 hunks)
  • tests/linter.spec.tsx (16 hunks)
  • tests/server.spec.tsx (12 hunks)
  • tests/theme.spec.tsx (2 hunks)
  • tests/util.spec.tsx (3 hunks)
💤 Files with no reviewable changes (4)
  • docs/examples/auto-clear.tsx
  • docs/demo/auto-clear.md
  • src/hooks/useCompatibleInsertionEffect.tsx
  • tests/legacy.spec.tsx
🧰 Additional context used
🧬 Code graph analysis (20)
src/util/index.ts (1)
src/StyleContext.tsx (1)
  • HashPriority (54-54)
tests/autoPrefix.spec.tsx (2)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
src/StyleContext.tsx (2)
  • StyleProvider (100-139)
  • createCache (16-52)
docs/examples/components/Spin.tsx (2)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
src/index.ts (1)
  • useStyleRegister (28-28)
docs/examples/autoPrefix.tsx (2)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
src/StyleContext.tsx (1)
  • StyleProvider (100-139)
tests/util.spec.tsx (1)
src/hooks/useStyleRegister.tsx (1)
  • normalizeStyle (83-89)
tests/linter.spec.tsx (2)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/hooks/useStyleRegister.tsx (2)
  • CSSObject (74-77)
  • useStyleRegister (375-520)
tests/index.spec.tsx (3)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/StyleContext.tsx (2)
  • StyleProvider (100-139)
  • ATTR_MARK (10-10)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
tests/keyframes.spec.tsx (1)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/StyleContext.tsx (1)
src/transformers/autoPrefix.ts (1)
  • AUTO_PREFIX (3-3)
src/transformers/autoPrefix.ts (1)
src/index.ts (1)
  • Transformer (59-59)
docs/examples/css-var.tsx (2)
docs/examples/ssr-advanced.tsx (1)
  • Demo (9-43)
docs/examples/components/theme.tsx (1)
  • DesignTokenProvider (64-93)
tests/animation.spec.tsx (2)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/StyleContext.tsx (2)
  • StyleProvider (100-139)
  • createCache (16-52)
src/hooks/useStyleRegister.tsx (4)
src/util/cacheMapUtil.ts (4)
  • serialize (12-19)
  • existPath (65-69)
  • getStyleAndHash (71-95)
  • CSS_FILE_STYLE (10-10)
src/hooks/useGlobalCache.tsx (2)
  • useGlobalCache (18-106)
  • ExtractStyle (7-14)
src/util/index.ts (2)
  • isClientSide (156-156)
  • toStyleStr (165-190)
src/StyleContext.tsx (3)
  • ATTR_MARK (10-10)
  • CSS_IN_JS_INSTANCE (14-14)
  • ATTR_CACHE_PATH (11-11)
src/util/css-variables.ts (2)
src/StyleContext.tsx (1)
  • HashPriority (54-54)
src/util/index.ts (1)
  • where (192-202)
docs/examples/components/Button.tsx (2)
docs/examples/components/theme.tsx (2)
  • DerivativeToken (23-25)
  • useToken (95-128)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
docs/examples/components/theme.tsx (1)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
tests/theme.spec.tsx (1)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/hooks/useCacheToken.tsx (4)
src/util/index.ts (2)
  • flattenToken (39-63)
  • token2key (68-70)
src/hooks/useGlobalCache.tsx (1)
  • useGlobalCache (18-106)
src/util/css-variables.ts (1)
  • transformToken (39-94)
src/StyleContext.tsx (3)
  • ATTR_MARK (10-10)
  • CSS_IN_JS_INSTANCE (14-14)
  • ATTR_TOKEN (9-9)
tests/server.spec.tsx (5)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/util/cacheMapUtil.ts (2)
  • reset (27-30)
  • getStyleAndHash (71-95)
src/extractStyle.ts (1)
  • extractStyle (32-111)
src/StyleContext.tsx (1)
  • createCache (16-52)
tests/css-variables.spec.tsx (2)
docs/examples/components/theme.tsx (1)
  • DesignTokenContext (54-62)
src/StyleContext.tsx (1)
  • StyleProvider (100-139)
🪛 Biome (2.1.2)
tests/server.spec.tsx

[error] 78-78: Shouldn't redeclare 'genCardStyle'. Consider to delete it or rename it.

'genCardStyle' is defined here:

(lint/suspicious/noRedeclare)

🪛 GitHub Actions: ✅ test
tests/index.spec.tsx

[error] 449-449: TS2345: Argument of type '{ salt: string; }' is not assignable to parameter of type 'Option<DerivativeToken, DerivativeToken>'. Property 'cssVar' is missing in type '{ salt: string; }'.


[error] 469-469: TS2322: Type '{ children: Element[]; cache: Entity; container: HTMLDivElement; autoClear: true; }' is not assignable to type 'IntrinsicAttributes & Partial<Omit<StyleContextProps, "autoPrefix">> & { children?: ReactNode; }'. Property 'autoClear' does not exist on type 'IntrinsicAttributes & Partial<Omit<StyleContextProps, "autoPrefix">> & { children?: ReactNode; }'.

tests/server.spec.tsx

[error] 72-72: TS2451: Cannot redeclare block-scoped variable 'genCardStyle'.


[error] 78-78: TS2451: Cannot redeclare block-scoped variable 'genCardStyle'.


[error] 94-94: TS2451: Cannot redeclare block-scoped variable 'Card'.


[error] 104-104: TS2451: Cannot redeclare block-scoped variable 'Card'.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: preview
🔇 Additional comments (35)
src/hooks/useEffectCleanupRegister.ts (3)

2-3: LGTM!导入方式优化得很好。

从命名空间导入改为命名导入是更符合习惯的做法,并且对 DependencyList 使用 type 导入是 TypeScript 的最佳实践。


22-31: LGTM!useEffect 用法与新的导入方式保持一致。

直接使用导入的 useEffect 而不是通过 React 命名空间访问,代码更简洁。逻辑保持不变,实现正确。


6-6: 函数重命名已正确完成,所有引用均已清理。

验证结果确认:

  • 文件 src/hooks/useEffectCleanupRegister.ts 中的新函数名已正确导出
  • 搜索结果表明不存在旧函数名 useCleanupRegister 的任何残留引用
  • 没有发现对新函数名的导入,表明函数尚未在代码库中使用,或所有引用已完全更新
  • 破坏性变更已干净地实现,无遗留问题
src/extractStyle.ts (4)

63-63: 类型注解更新正确。

新增的 updateTime 字段准确反映了返回值结构,增强了类型安全性。


86-86: 返回值结构正确。

返回的三元组与第 63 行的类型注解一致,逻辑清晰。


77-77: cache.updateTimes 属性验证通过。

Cache 类在第 24 行正确定义了 updateTimes = new Map<string, number>(),类型与预期一致。第 77 行的代码 cache.updateTimes.get(key) || 0 是有效的,无需修改。


89-94: 排序逻辑正确,已由充分的测试验证。

代码在第 89-94 行的排序实现正确:先按 order 升序排列,若 order 相等则按 updateTime 升序排列。tests/server.spec.tsx 中的 "extract with order" 测试用例已覆盖此功能,通过 testOrder 函数验证了 6 种不同渲染顺序下样式的相对位置,确保排序逻辑在各种场景下都能正确工作。快照文件进一步记录了预期的输出结果。

src/linters/contentQuotesLinter.ts (1)

14-14: CSS 自定义属性异常处理正确。

正确添加了对 CSS 变量(var())的支持,允许以 var( 开头的值通过验证而无需引号。这符合 CSS 规范,因为 CSS 自定义属性引用不需要引号。

src/Cache.ts (3)

11-12: 全局更新 ID 计数器实现合理。

引入全局 updateId 计数器来为缓存更新分配单调递增的时间戳,这对于跟踪静态样式提取顺序很有用。


23-24: 每个 keyPath 的更新时间跟踪实现正确。

添加 updateTimes 映射来记录每个 keyPath 的更新时间戳,与全局 updateId 配合使用。


52-59: 更新和删除逻辑处理得当。

正确处理了缓存更新时的时间戳记录:

  • 删除值时同时清理 updateTimes
  • 设置值时记录当前 updateId 并递增计数器

这确保了缓存和时间戳的一致性。

src/util/index.ts (1)

192-202: where() 函数实现正确。

新增的 where() 函数根据 hashPriority 生成适当的选择器字符串:

  • hashPriority'low' 时使用 :where() 伪类降低选择器优先级
  • 否则返回原始选择器
  • 正确处理了 hashCls 不存在的情况

这对于控制 CSS 选择器优先级很有用。

package.json (4)

3-3: 版本号升级至 2.0.0-alpha.9。

这是一个主版本更新,表明引入了破坏性变更。确保所有相关文档和变更日志都已更新。


34-34: 发布脚本更新为 rc-np。

np 替换为 rc-np(@rc-component/np),这与项目依赖项的更新保持一致。


41-41: 添加了 TypeScript 类型检查脚本。

新增的 tsc --noEmit 脚本可以进行类型检查而不生成输出文件,这是一个很好的开发实践。


71-71: ESLint 8.57.1 版本选择合理。

验证结果显示,保持 ESLint 8.57.1 是有充分理由的:

  • ESLint 9 要求 Node.js >= 18.18,但项目 package.json 未指定 engines 字段,无法确认当前系统是否满足版本要求
  • 项目使用旧格式 .eslintrc.js 配置,ESLint 9 引入 flat config 作为默认配置格式,迁移需要重构配置
  • ESLint 9 包含多项破坏性变更(规则行为改变、移除 require-jsdoc 和 valid-jsdoc 等),需要全面测试
  • 项目依赖 @umijs/fabric 作为基础配置,其 ESLint 9 兼容性未知

若将来需要升级至 v9,建议先:添加 Node.js 版本约束、迁移到 flat config 格式、验证所有规则的兼容性。

docs/demo/first-render.md (1)

1-8: 首次渲染演示文档结构正确。

新增的文档文件结构标准,正确引用了示例代码。文档标题和导航配置清晰。

src/index.ts (1)

19-19: 正确添加了 autoPrefixTransformer 到公共 API。

新的转换器已正确导入并导出,与现有的其他转换器(如 legacyLogicalPropertiesTransformerpx2remTransformer)保持一致的结构。

Also applies to: 39-39

docs/examples/components/Spin.tsx (1)

44-51: 移除 wrapSSR 模式简化了代码。

更新后的代码直接调用 useStyleRegister 进行样式注册(仅用于副作用),并直接返回元素,不再使用 SSR 包装器。这与 PR 中引入的新样式注册和 CSS 变量处理流程保持一致。

tests/util.spec.tsx (1)

43-43: 正确更新了 normalizeStyle API 调用。

所有 normalizeStyle 调用都已更新以传递新的布尔参数(autoPrefix 标志)。三个调用点都统一传递 false,与函数签名的更改保持一致。测试的预期输出保持不变,表明更新是正确的。

Also applies to: 99-99, 160-160

tests/theme.spec.tsx (2)

115-117: LGTM!

useCacheToken 的调用已正确更新,添加了必需的 cssVar 选项参数。这与 src/hooks/useCacheToken.tsx 中的新签名一致。


115-119: 确认解构模式的意图。

解构模式从 [token, hashId] 更改为 [, , realToken],跳过了前两个元素以获取第三个元素(actualToken)。根据 useCacheToken 的返回值 [tokenWithCssVar, hashCls, actualToken, cssVarsStr, cssVar.key],这里获取的是原始的 actualToken 而不是 tokenWithCssVar。

请确认测试中需要使用 actualToken 而不是 tokenWithCssVar 是否符合预期。如果测试的目的是验证 CSS 变量转换前的原始 token,那么这是正确的。

tests/keyframes.spec.tsx (1)

39-39: LGTM!

已正确添加 cssVar 选项以匹配更新后的 useCacheToken API。

docs/examples/autoPrefix.tsx (1)

1-51: LGTM!

示例代码结构清晰,正确演示了如何使用 autoPrefixTransformer。包含的 CSS 属性(transform、userSelect、display、backdropFilter 等)都是通常需要供应商前缀的属性。

docs/examples/css-var.tsx (1)

4-4: LGTM! 确认第一个 Demo 实例的行为是否符合预期。

代码已成功从 DesignTokenContext 迁移到 DesignTokenProvider。第 37 行的第一个 <Demo /> 实例没有被 DesignTokenProvider 包裹,这可能是有意为之,用于展示默认行为与使用 provider 时的对比。

如果这是预期行为,建议添加注释说明。

Also applies to: 39-54

src/hooks/useCSSVarRegister.ts (1)

33-33: LGTM!

添加 hashId 参数以及将 hashPriorityhashCls 传递给 transformToken 的更改是正确的,与 CSS 变量哈希功能的增强一致。

Also applies to: 37-37, 41-41, 57-58

tests/index.spec.tsx (1)

872-948: 测试覆盖良好!

新增的测试用例全面验证了哈希值仅在 salt 或 cssVar prefix 变化时改变,而不会因 token 值变化而改变。这确保了样式缓存的正确性和性能优化。

注意:Lines 926 和 938 包含中文注释,建议根据团队代码规范考虑是否需要统一为英文。

src/StyleContext.tsx (1)

83-84: autoPrefix 功能设计合理。

autoPrefix 作为内部标志并通过 transformers 自动设置,而不是作为公共 API 暴露,这是一个良好的设计决策。注释清楚地解释了这种方法的原因。

docs/examples/components/theme.tsx (1)

95-127: useToken API 扩展清晰且有用。

扩展返回值以包含 cssVar.keyactualToken 为使用者提供了更大的灵活性。这允许组件同时访问 CSS 变量转换后的 token 和原始 token 值。

src/util/css-variables.ts (3)

4-10: token2CSSVar 转换逻辑正确。

函数通过多个正则替换正确处理了各种 camelCase 到 kebab-case 的转换场景,包括边界情况(如连续大写字母)。实现健壮。


12-30: CSS 变量序列化的哈希支持实现良好。

通过 where() 函数集成哈希类,为 CSS 变量提供了主题隔离能力,同时使用 :where() 保持较低的选择器优先级,避免样式冲突。


39-94: transformToken 重构清晰且保持向后兼容。

提前解构所有配置选项使代码更清晰,同时通过可选链保持了向后兼容性。哈希相关选项被正确地传递给 serializeCSSVar

docs/examples/components/Button.tsx (3)

14-17: ButtonToken 接口定义清晰。

通过扩展 DerivativeToken 并添加按钮特定的 token,展示了组件级 token 扩展的良好模式。


126-145: useCSSVarRegister 集成示例完整且有用。

展示了如何使用 useCSSVarRegister 注册组件级 CSS 变量,包括动态计算(如 calc() 表达式)和适当的作用域限制。这是其他组件开发者的良好参考。


147-157: token 合并和样式注册流程正确。

将基础 token 与组件级 CSS 变量 token 合并后传递给样式生成器,确保了组件样式可以正确访问所有必需的 token 值。

Comment on lines +64 to +93
export const DesignTokenProvider: React.FC<
PropsWithChildren<{
value?: {
token?: Partial<DesignToken>;
hashed?: string | boolean;
cssVar?: {
key?: string;
prefix?: string;
};
};
}>
> = ({ children, value }) => {
const { token, hashed, cssVar } = value || {};
const themeKey = React.useId();
const cssVarKey = `css-var-${themeKey.replace(/:/g, '')}`;
return (
<DesignTokenContext.Provider
value={{
token,
hashed,
cssVar: {
...cssVar,
key: cssVar?.key || cssVarKey,
},
}}
>
{children}
</DesignTokenContext.Provider>
);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

DesignTokenProvider 实现良好,但需注意 React 版本依赖。

DesignTokenProvider 使用 React.useId() 生成唯一的 CSS 变量键,这是一个良好的实践。然而,useId() 是 React 18 引入的 API。

根据 PR 摘要,此版本已移除 React 18 以下版本的兼容性支持,因此使用 useId() 是安全的。建议确认 package.json 中的 React 最低版本要求是否已更新为 18.0.0 或更高:


🏁 Script executed:

#!/bin/bash
# 检查 package.json 中的 React 版本要求
cat package.json | jq -r '.peerDependencies.react, .devDependencies.react, .dependencies.react' | head -3

Length of output: 21


需要进一步验证 React.useId() 在代码库中的使用范围和这是否是一个新的breaking change。让我检查相关信息。


---

🏁 Script executed:

```shell
#!/bin/bash
# 检查代码库中是否还有其他地方使用 React.useId()
rg "useId" -n -C2

Length of output: 614


🏁 Script executed:

#!/bin/bash
# 检查 package.json 中是否有 breaking changes 的说明或版本号变化
cat package.json | jq -r '.version'

Length of output: 13


更新 package.json 中的 React peerDependencies 版本要求。

DesignTokenProvider 使用了 React.useId()(React 18+ API),但 package.jsonpeerDependencies 仍声明为 >=16.0.0,允许 React 16 及更早版本。这会导致在 React 16/17 环境中发生运行时错误。

package.json 中,需要将 peerDependencies.react>=16.0.0 更新为 ^18.0.0>=18.0.0,以匹配代码的实际要求。代码库中至少有两处使用了 React.useId()docs/examples/components/theme.tsx:77tests/server.spec.tsx:115)。

🤖 Prompt for AI Agents
In docs/examples/components/theme.tsx around lines 64 to 93, the component uses
React.useId() (React 18+), but package.json still declares
peerDependencies.react as >=16.0.0 allowing older React versions; update
package.json to require React 18+ (e.g., set peerDependencies.react to "^18.0.0"
or ">=18.0.0") so consumers cannot install with React 16/17, and run a quick
grep to confirm other instances (tests/server.spec.tsx:115 and any others) match
this requirement.

Comment on lines +1 to +7
import type { Transformer } from './interface';

export const AUTO_PREFIX = {};

const transform: Transformer = AUTO_PREFIX;

export default transform;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

验证空转换器的设计意图。

此转换器导出一个空对象,不包含任何实际的转换逻辑。根据 src/StyleContext.tsx 的代码片段,AUTO_PREFIX 似乎只是作为一个标记值使用,实际的前缀添加是通过 normalizeStyle 函数的 autoPrefix 布尔标志完成的。

请确认这是否是有意为之的设计,还是未完成的实现。如果这只是一个标记,建议在代码中添加注释说明其用途,以避免未来的混淆。


🌐 Web query:

Does @ant-design/cssinjs use stylis or another library for CSS autoprefixing in the normalizeStyle function?

💡 Result:

Short answer: It uses stylis. The @ant-design/cssinjs package lists stylis as a dependency and normalizeStyle calls stylis APIs (see the reported error referencing stylis.compile). [1][2]

Sources:

  • @ant-design/cssinjs README (lists stylis as a dependency). [1]
  • StackOverflow issue showing normalizeStyle invoking stylis (error trace referencing stylis.compile). [2]

Thank you for providing the web search result. The confirmation that @ant-design/cssinjs uses stylis aligns with the reviewer's suspicion. Now I need to verify the actual usage pattern of AUTO_PREFIX in the codebase to understand if the empty object design is intentional.


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for AUTO_PREFIX usage patterns across the codebase
rg -n "AUTO_PREFIX" --type ts --type tsx -B 2 -A 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check src/StyleContext.tsx to see the context of AUTO_PREFIX usage
fd "StyleContext" --type ts --type tsx -x cat {}

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Look for other transformer implementations to understand the pattern
fd "Transformer" --type ts --type tsx -x grep -l "Transformer" {}

Length of output: 169



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for AUTO_PREFIX usage with corrected syntax
rg -n "AUTO_PREFIX" -A 2 -B 2

Length of output: 951


🏁 Script executed:

#!/bin/bash
# Find and read StyleContext file
fd "StyleContext" -x cat {}

Length of output: 4561


🏁 Script executed:

#!/bin/bash
# Find the Transformer interface definition
rg -n "interface Transformer" -A 5 -B 1

Length of output: 205


🏁 Script executed:

#!/bin/bash
# Look for other transformer implementations to understand pattern
rg -n "const.*Transformer\s*=" -A 3 -B 1

Length of output: 874


在代码中添加注释以说明 AUTO_PREFIX 的设计意图。

验证确认了您的怀疑是正确的:AUTO_PREFIX 确实是一个标记值,而非真正的转换器。

根据代码分析:

  • AUTO_PREFIXStyleContext.tsx 第 125 行被用作哨兵值检测:当 transformers 数组中包含 AUTO_PREFIX 时,设置 autoPrefix 布尔标志为 true
  • 实际的 CSS 自动前缀添加由 @ant-design/cssinjs 中的 stylis 库通过 normalizeStyle 函数完成,而非通过 Transformer.visit() 方法
  • 与其他转换器(如 legacyLogicalPropertiespx2rem)不同,AUTO_PREFIX 是一个设计特殊的空对象,符合 Transformer 接口的可选 visit 方法定义

建议在 src/transformers/autoPrefix.ts 中添加注释,说明 AUTO_PREFIX 是一个标记值,用于启用自动前缀功能,实际的前缀添加由 normalizeStyle 函数处理。

🤖 Prompt for AI Agents
In src/transformers/autoPrefix.ts around lines 1 to 7, add a concise comment
explaining that AUTO_PREFIX is a sentinel/marker object (not a real Transformer
implementation) used to enable automatic CSS prefixing; note that
StyleContext.tsx checks for this object to set the autoPrefix flag and that
actual prefixing is performed by normalizeStyle (stylis) from
@ant-design/cssinjs rather than via Transformer.visit(). Make the comment short,
mention why the object is intentionally empty and that it conforms to the
optional Transformer shape to be presentable in the transformers array.

Comment on lines +28 to +56
it('should add vendor prefixes when autoPrefix transformer is used', () => {
const css: CSSInterpolation = {
'.box': {
transform: 'translateX(10px)',
userSelect: 'none',
display: 'flex',
},
};

render(
<StyleProvider
cache={createCache()}
transformers={[autoPrefixTransformer]}
>
<Demo css={css} />
</StyleProvider>,
);

const styles = Array.from(document.head.querySelectorAll('style'));
expect(styles).toHaveLength(1);

const styleText = styles[0].innerHTML;
console.log('AutoPrefix test output:', styleText);

// Check that the CSS contains prefixed properties
expect(styleText).toContain('transform:translateX(10px)');
expect(styleText).toContain('display:flex');
expect(styleText).toContain('user-select:none');
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

测试断言不完整,未验证供应商前缀。

当前测试仅检查未加前缀的属性是否存在,但没有验证是否实际添加了供应商前缀(如 -webkit--moz- 等)。这无法确认 autoPrefixTransformer 是否按预期工作。

建议添加断言来验证生成的 CSS 是否包含带前缀的属性,例如:

     // Check that the CSS contains prefixed properties
     expect(styleText).toContain('transform:translateX(10px)');
+    expect(styleText).toMatch(/-webkit-transform:translateX\(10px\)/);
     expect(styleText).toContain('display:flex');
+    expect(styleText).toMatch(/-webkit-(box|flex)/); // flex or box model
     expect(styleText).toContain('user-select:none');
+    expect(styleText).toMatch(/-webkit-user-select:none/);
+    expect(styleText).toMatch(/-moz-user-select:none/);

注意:具体的前缀取决于所使用的自动前缀库(如 stylis、autoprefixer)的行为。

📝 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
it('should add vendor prefixes when autoPrefix transformer is used', () => {
const css: CSSInterpolation = {
'.box': {
transform: 'translateX(10px)',
userSelect: 'none',
display: 'flex',
},
};
render(
<StyleProvider
cache={createCache()}
transformers={[autoPrefixTransformer]}
>
<Demo css={css} />
</StyleProvider>,
);
const styles = Array.from(document.head.querySelectorAll('style'));
expect(styles).toHaveLength(1);
const styleText = styles[0].innerHTML;
console.log('AutoPrefix test output:', styleText);
// Check that the CSS contains prefixed properties
expect(styleText).toContain('transform:translateX(10px)');
expect(styleText).toContain('display:flex');
expect(styleText).toContain('user-select:none');
});
it('should add vendor prefixes when autoPrefix transformer is used', () => {
const css: CSSInterpolation = {
'.box': {
transform: 'translateX(10px)',
userSelect: 'none',
display: 'flex',
},
};
render(
<StyleProvider
cache={createCache()}
transformers={[autoPrefixTransformer]}
>
<Demo css={css} />
</StyleProvider>,
);
const styles = Array.from(document.head.querySelectorAll('style'));
expect(styles).toHaveLength(1);
const styleText = styles[0].innerHTML;
console.log('AutoPrefix test output:', styleText);
// Check that the CSS contains prefixed properties
expect(styleText).toContain('transform:translateX(10px)');
expect(styleText).toMatch(/-webkit-transform:translateX\(10px\)/);
expect(styleText).toContain('display:flex');
expect(styleText).toMatch(/-webkit-(box|flex)/); // flex or box model
expect(styleText).toContain('user-select:none');
expect(styleText).toMatch(/-webkit-user-select:none/);
expect(styleText).toMatch(/-moz-user-select:none/);
});
🤖 Prompt for AI Agents
In tests/autoPrefix.spec.tsx around lines 28 to 56, the test only asserts
unprefixed properties and therefore doesn't verify that autoPrefixTransformer
actually adds vendor prefixes; update the assertions to also check for expected
vendor-prefixed forms (e.g. '-webkit-transform', '-webkit-user-select',
'-moz-user-select' or other prefixes your autoprefixer produces) by asserting
styleText contains those prefixed declarations in addition to the current
checks; if prefix output varies by environment, assert for at least one known
prefix per property or use a regex to match either prefixed or unprefixed
variants.

expect(styles).toHaveLength(1);

const styleText = styles[0].innerHTML;
console.log('AutoPrefix test output:', styleText);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

移除调试用的 console.log 语句。

测试代码中不应保留 console.log 调试语句。

应用此差异:

-    console.log('AutoPrefix test output:', styleText);
📝 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
console.log('AutoPrefix test output:', styleText);
🤖 Prompt for AI Agents
In tests/autoPrefix.spec.tsx around line 50, there is a leftover debugging
console.log('AutoPrefix test output:', styleText); — remove this console.log
statement from the test file (or replace it with an assertion or proper test
helper if the output needs verification) so no debug logging remains in the test
code.

Comment on lines 78 to 102
const genCardStyle = (token: DerivativeToken): CSSInterpolation => ({
'.card': {
backgroundColor: token.primaryColor,
},
});

const Box = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken]);
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
});

const wrapSSR = useStyleRegister({ theme, token, path: ['.box'] }, () => [
genStyle(token),
]);
useStyleRegister({ theme, token, path: ['.box'] }, () => [genStyle(token)]);

return <div className="box">{children}</div>;
};

const Card = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
});

return wrapSSR(<div className="box">{children}</div>);
useStyleRegister({ theme, token, path: ['.card'] }, () => [genCardStyle(token)]);

return <div className="card">{children}</div>;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

移除重复定义以恢复编译
Line 78-102 新增了一套 genCardStyleCard 定义,但原有定义仍然保留,导致同一作用域内重复声明,正如流水线报错 TS2451: Cannot redeclare block-scoped variable 'genCardStyle' / 'Card' 所示,构建被完全阻断。请删除新增的重复块,只保留其中一份实现。建议修改如下:

-const genCardStyle = (token: DerivativeToken): CSSInterpolation => ({
-  '.card': {
-    backgroundColor: token.primaryColor,
-  },
-});
-
-const Card = ({ children }: { children?: React.ReactNode }) => {
-  const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
-    cssVar: { key: 'css-var-test' },
-  });
-
-  useStyleRegister({ theme, token, path: ['.card'] }, () => [genCardStyle(token)]);
-
-  return <div className="card">{children}</div>;
-};
📝 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 genCardStyle = (token: DerivativeToken): CSSInterpolation => ({
'.card': {
backgroundColor: token.primaryColor,
},
});
const Box = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken]);
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
});
const wrapSSR = useStyleRegister({ theme, token, path: ['.box'] }, () => [
genStyle(token),
]);
useStyleRegister({ theme, token, path: ['.box'] }, () => [genStyle(token)]);
return <div className="box">{children}</div>;
};
const Card = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
});
return wrapSSR(<div className="box">{children}</div>);
useStyleRegister({ theme, token, path: ['.card'] }, () => [genCardStyle(token)]);
return <div className="card">{children}</div>;
};
🧰 Tools
🪛 Biome (2.1.2)

[error] 78-78: Shouldn't redeclare 'genCardStyle'. Consider to delete it or rename it.

'genCardStyle' is defined here:

(lint/suspicious/noRedeclare)

🪛 GitHub Actions: ✅ test

[error] 78-78: TS2451: Cannot redeclare block-scoped variable 'genCardStyle'.


[error] 94-94: TS2451: Cannot redeclare block-scoped variable 'Card'.

🤖 Prompt for AI Agents
In tests/server.spec.tsx around lines 78 to 102 there are duplicate declarations
of genCardStyle and Card in the same scope causing TS2451 redeclare errors;
remove the duplicated block (either the newly added genCardStyle/Card or the
original ones) so each identifier is declared only once, and ensure imports/uses
still reference the retained implementation.

Copy link

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/server.spec.tsx (1)

189-203: Client 组件 className 与注册的选择器不一致,容易造成困惑

Client 组件通过 useStyleRegister 注册的是 '.client' 规则,但返回的 DOM 元素却是 className="box"。当前用例只验证 extractStyle 的结果不包含 'client',因此功能上不受影响,不过命名和样式选择器不一致比较迷惑。建议将返回元素的 className 与样式 path 对齐(例如统一用 client),或者在测试里说明这是刻意行为,以提升可读性。

src/hooks/useCacheToken.tsx (1)

24-31: Option.cssVar 改为必填属于破坏性变更,建议在对外文档中明确

Option<DerivativeToken, DesignToken> 现在要求调用方必须提供 cssVar,且其中的 key 也变为必填。这对库的内部调用(测试、示例)已经全部调整,但对外部使用者来说:

  • 之前可选的第三个参数现在缺省会导致运行时报错;
  • 没有配置 cssVar 的老代码需要至少补上一个 { cssVar: { key: 'xxx' } } 才能工作。

从实现角度这是合理的(新架构强依赖 cssVar),但建议:

  • 在 CHANGELOG / 迁移指南中明确标注这是 2.x 的 breaking change;
  • 简要说明 cssVar.key 的语义(主题级 key),以及推荐如何命名和复用。

有助于使用者升级时少踩坑。

Also applies to: 55-71, 152-159, 166-171

♻️ Duplicate comments (5)
src/Cache.ts (1)

11-12: 注意:全局可变的 updateId 在 SSR 场景下的并发风险

如之前的评审所指出,这个全局可变的 updateId 计数器在服务端并发渲染时可能导致竞态条件。如果多个请求共享同一个 cache 实例,更新顺序可能会出错。建议添加注释说明此变量的使用场景和安全前提(例如每个 SSR 请求创建独立的 cache 实例)。

tests/autoPrefix.spec.tsx (1)

50-50: 移除调试用的 console.log 语句

如之前评审所指出,这个 console.log 语句应该从最终的测试代码中移除,以保持测试输出的整洁。

docs/examples/components/Button.tsx (1)

147-157: 收紧 mergedToken 类型以提升类型安全(与之前评论相同方向)

这里已经定义了 ButtonToken,但仍然将 mergedToken 标注为 any,会削弱后续样式函数(已使用 ButtonToken)的类型约束。建议直接使用 ButtonToken

-  const mergedToken: any = {
-    ...token,
-    ...cssVarToken,
-  };
+  const mergedToken: ButtonToken = {
+    ...(token as DerivativeToken),
+    ...(cssVarToken as Partial<ButtonToken>),
+  };

这样可以在保持行为不变的前提下,让 genDefaultButtonStyle 等函数获得完整的静态类型信息。

src/hooks/useGlobalCache.tsx (1)

12-16: 全局 effectMap 的作用建议在代码中补充注释,说明并发/SSR 假设

这里通过 module 级别的 effectMap 配合 useInsertionEffectPromise.resolve().then,实现了“同一 fullPathStr 在一个微任务批中只触发一次 onCacheEffect”的策略,从而避免重复注入样式,逻辑上是合理的。不过 effectMap 作为全局可变状态,在并发或多请求 SSR 场景下容易让人担心作用域问题(虽然当前实现依赖于单实例、单文档的假设)。建议在 effectMap 定义附近补一段注释,明确说明:

  • 它按 fullPathStr 仅在当前事件循环批次内做一次去重;
  • 通过微任务和引用计数,生命周期被严格控制;
  • 对 SSR 的使用场景有什么前提假设。

这样后续维护者在修改 useGlobalCache / useInsertionEffect 逻辑时更不容易踩坑。如果仅用于去重,其实也可以考虑改为 Set<string> 来表达 intent 更直观。

Also applies to: 76-104

src/hooks/useCacheToken.tsx (1)

73-76: TOKEN_THRESHOLD / tokenKeys 清理策略建议补充注释说明,同时注意多实例场景

当前逻辑为:

  • recordCleanToken(cssVar.key) 在构建缓存时按 cssVar.key 全局计数
  • cleanTokenStyle 在最后一个使用者释放时调用,先把 tokenKeys 中计数减一,再根据 value <= 0 收集可清理 key;
  • TOKEN_THRESHOLD = -1 使得只要 tokenKeys.size - cleanableKeyList.size >= 0,都会对计数小于等于 0 的 key 执行清理。

这有两个点值得注意:

  1. 这段逻辑的设计目的(例如为了配合 React 18 双渲染 / remount 行为延后或提前清理)从代码本身并不直观,后续维护者很难一眼看出为什么是 -1,建议在 TOKEN_THRESHOLDcleanTokenStyle 附近补一个简短注释说明设计意图和期望场景。
  2. tokenKeyscssVar.key 为维度全局计数,而 removeStyleTags 又按 instanceId 过滤删除 <style>。如果项目中出现「多个 cache 实例但复用同一个 cssVar.key」的情况,可能出现某些实例对应的 <style> 在计数尚未归零时不会被清掉,最后一个实例释放时只会删除最后那个实例插入的样式,之前实例留下的样式会滞留在 DOM 中。这个场景目前在测试里似乎没有覆盖,建议至少在注释中点明「cssVar.key 建议在全局范围内唯一且对应单一 cache 实例」,或者未来将计数 key 扩展为 (cssVar.key, instanceId)

此前已有 reviewer 提醒过这里需要解释 threshold 选择的原因,本条属于对该建议的补充和细化。

Also applies to: 90-103

🧹 Nitpick comments (17)
src/hooks/useEffectCleanupRegister.ts (1)

1-36: LGTM!重命名提高了代码清晰度。

将 hook 重命名为 useEffectCleanupRegister 更明确地表达了其使用 useEffect 的特性。直接导入 React hooks 而非使用命名空间也更符合现代 React 代码风格。

src/transformers/autoPrefix.ts (1)

1-7: autoPrefix Transformer 定义简洁,符合占位配置的设计

当前实现等价于导出一个空配置对象,由内部根据 Transformer 类型和 normalizeStyle 行为决定是否启用自动前缀,看不出明显逻辑或类型问题。如需进一步加强类型安全,可以考虑直接为 AUTO_PREFIX 标注 Transformer 类型,但这属于可选优化。

tests/server.spec.tsx (3)

79-85: Box 组件中重复配置 cssVar 可以考虑抽取工具函数

这段对 useCacheTokenuseStyleRegister 的改动与当前 cssVar API 一致,行为上看没有问题。不过 { cssVar: { key: 'css-var-test' } } 在 Box、Card 以及后面几个测试组件里多次重复出现,后续如果 key 或配置变动,容易漏改。可以考虑在文件顶部抽一个小的 const cssVarOption = { cssVar: { key: 'css-var-test' } } 复用,简化测试代码。


124-126: SSR 断言与内部实现耦合稍紧,可适当放宽匹配条件

这里对 htmlstyleplainStyle 采用快照断言,同时对 getStyleAndHash 的返回值做了精确字符串匹配(包括 .box{background-color:var(--primary-color);} 和固定哈希 "1bbkdf1")。这会把测试直接绑死在当前 cssVar 生成规则、哈希算法以及样式串格式上,未来只要内部实现稍作调整(比如前缀或压缩策略变化),就需要频繁更新快照和断言。可以考虑只断言关键特征,例如包含 background-color:var(--primary-color) 和正确的路径 key,而不是整串完全相等,以降低对内部实现细节的耦合度。

Also applies to: 138-138, 171-176, 181-181


321-323: 建议在 ssr keep order 用例中显式断言样式数量

ssr keep order 用例通过 styles[1..3].getAttribute('data-rc-priority') 断言 0/1/2,但没有先验证 styles.length 是否至少为 4。如果未来实现调整导致生成的 <style> 数量减少,styles[3] 会是 undefined,断言失败时错误信息不够直观。可以在检查 priority 之前增加一条 expect(styles.length).toBeGreaterThanOrEqual(4),方便定位问题。

Also applies to: 325-329, 331-331, 356-358, 362-362

tests/css-variables.spec.tsx (2)

65-77: DesignTokenContext 默认值与类型定义基本一致,可考虑放宽 cssVar 可选性

这里将 DesignTokenProviderProps.cssVar 定义为必填,并在 DesignTokenContext 默认值里补上 hashed: falsecss-var-root 作为默认 key,整体上行为一致且明确。不过如果未来在别处复用 DesignTokenProviderProps,强制要求调用方总是显式传 cssVar,可能略显严格;从模式上看,也可以考虑把类型改成可选并在 Provider 内部统一补默认值,和这里的 Context 默认实现保持一致。当前测试文件自身的使用是安全的,这点更多是类型层面的可用性建议。


276-312: “could autoClear” 用例的命名与当前断言语义略不一致

这个测试现在验证的是:卸载 Box 后仍然保留一份包含 .box{} 的样式,而不是彻底清空所有相关样式块。注释 // component style still remains 已经说明了这一点,但用例名字仍叫 could autoClear,容易让人误解为“组件样式会被完全清除”。可以考虑把测试名改成更贴近行为的描述,例如 “autoClear 只清理部分样式” 或类似表述,以免未来阅读时产生歧义。

tests/animation.spec.tsx (2)

56-57: 未 hashed 场景样式数量增加合理,可考虑补充对 cssVar 块的简单校验

Box 中为 useCacheToken 增加了 cssVar 配置,render<style> 数量从 2 变为 3,并继续对索引 1 和 2 的动画样式/关键帧做精确断言,这与新增的 cssVar 样式块位于索引 0 的假设是匹配的。为了让测试在未来对样式插入顺序更具自解释性,可以适度增加一条弱断言(例如验证 styles[0].innerHTML 至少包含某个 var(-- 或 cssVar key 片段),从而说明第一个样式块是为 cssVar 保留的。

Also applies to: 70-73


84-85: hashed 动画测试整体逻辑正确,但断言依赖具体字符串格式

这些用例在 hashed 模式下为动画相关样式断言了完整的 innerHTML 字符串(包括 :where(.${hashId}) 选择器和 @keyframes ${hashId}-anim),从逻辑上完全符合当前实现,能有效防止回归。不过这也意味着测试高度依赖样式串的精确格式,未来如果内部格式化或压缩策略有调整(例如多一个空格、属性排序变化),可能需要频繁同步更新测试。可以在保持主要用例不变的同时,在新增用例中更多采用 toContain 之类的部分匹配,平衡精确性与维护成本。

Also applies to: 95-103, 109-110, 120-127, 134-135, 145-152, 159-161, 171-178

docs/examples/components/theme.tsx (2)

64-93: DesignTokenProvider 默认生成 cssVar key 的行为清晰,可在文档中强调一下

新增加的 DesignTokenProvider 会在未显式传入 cssVar.key 时基于 useId() 自动生成一个形如 css-var-<id> 的 key,并通过 Context 下发。这让 useToken 始终能拿到一个稳定的 cssVar key,整体设计合理。不过这个行为对使用者来说相对“隐式”,建议在示例文档中简单说明:不传 key 时会自动生成一个与 Provider 实例绑定的 key,便于理解为什么最终类名中会多出类似 css-var-xxxx 的标记。


95-101: useToken 扩展为返回 5 元组,需要同步更新调用方的解构顺序

useToken 现在返回 [theme, token, hashId, cssVarKey, actualToken] 五个值,并固定使用 'rc' 作为 cssVar prefix。这个扩展能让下游组件同时拿到包含 cssVar 的 token 和原始 actualToken,设计上是合理的。但因为顺序与原来的 3/4 元组不兼容,示例或业务代码如果还按旧顺序解构(例如只取前三个值),就可能出现错位。建议检查一下文档/示例中所有对 useToken 的使用是否已经更新到新的解构顺序,避免读者照抄旧示例导致困惑。

Also applies to: 105-105, 109-112, 113-127

src/hooks/useStyleRegister.tsx (1)

364-371: StyleCacheValue 元组结构可读性一般,后续可以考虑改为对象

当前 StyleCacheValue 定义为 [styleStr, styleId, effectStyle, clientOnly, order],在创建 cache、HMR 清理、注入样式和 extract 中都通过按位置解构,跨文件使用较多:

  • cacheFn 返回 [styleStr, styleId, effectStyle, clientOnly, order]
  • HMR 清理只取索引 1 的 styleId
  • effect 回调中解构为 [styleStr, styleId, effectStyle, , priority]
  • extract 再次按同样顺序解构

这种「多处依赖魔法下标」的写法对维护者不太友好,将来如果再扩展元组内容,很容易因为漏改某一个地方导致微妙 bug。后续如果有机会,可以考虑把 StyleCacheValue 升级成具名对象(例如 { styleStr, styleId, effectStyle, clientOnly, order }),即便保留元组也建议加一段注释集中说明各位置含义,降低心智负担。

Also applies to: 417-423, 450-460, 458-519, 522-528

src/util/css-variables.ts (2)

1-3: serializeCSSVar 中参数命名略有迷惑,建议用更语义化名称

这里 serializeCSSVar(cssVars, hashId, options) 中第二个参数在实现里其实代表的是「主题级选择器 key」(例如 'css-var-test'),而不是我们通常理解的「hashId」,最终输出形如:

:where(.hashCls).css-var-test{...}

为了减少误解,后续重构时可以考虑:

  • 把形参从 hashId 重命名为更贴切的 themeKeyclassName
  • 或者在函数上方补一行注释,说明第二个参数含义是「cssVar 容器 class」,而非散列值。

当前实现本身是正确的,只是命名容易让人联想到 ATTR_MARK 的 hash。

Also applies to: 12-22, 25-30


39-60: transformToken 的 hashCls/hashPriority 透传设计合理,建议在注释中强调用途

transformToken 现在把 hashClshashPriority 一路传到 serializeCSSVar 并利用 where({ hashCls, hashPriority }) 包裹变量块,从而实现:

  • cssVar.hashed 为真时,变量块受 hash class 作用域控制(低优先级时通过 :where() 包裹);
  • hashed 关闭时,回退到没有 hash class 的 .themeKey{--var:...}

逻辑上是合理的,不过这两个选项的意图(特别是「为什么变量块需要 hash 作用域」)对后来者不算直观,建议在 transformTokencss-variables.ts 顶部加一段块注释,简要说明:

  • hashCls 与组件 hashId 的关系;
  • hashPriority 为何默认 'low',以及何时需要切到 'high'

有助于未来维护者理解这些参数从 StyleContext 传过来的目的。

Also applies to: 61-69, 86-93

tests/index.spec.tsx (2)

88-96: 基于 innerHTML 的断言较多,后续可以逐步收敛到更稳定的匹配方式

目前不少用例直接断言 styles[i].innerHTML 完整等于某个字符串或快照(特别是包含顺序敏感的长 CSS 文本场景),例如:

  • 变量块内容和顺序完全匹配;
  • .box{...} 整串样式的 strict equal / snapshot;
  • 复杂场景中多次快照对同一个 innerHTML 进行比较。

在行为变更较大的阶段这种断言很有帮助,但长期来看:

  • 对内部实现的小调整(例如格式化、属性排序、空格)会比较脆弱;
  • 也不利于只关心「存在某些关键声明」而忽略其他细节的场景。

建议后续迭代时,可以按优先级逐步把一部分断言收敛为:

  • 使用 toContain 或正则只匹配关键变量 / 关键属性;
  • 对顺序不敏感的情况避免整体 toEqual / snapshot。

现阶段这些断言对本次功能确认是有价值的,可以先保留。

Also applies to: 190-197, 305-313, 661-665, 669-672, 681-683, 737-752, 815-826, 913-949


248-250: 重复快照断言可以精简

这里对同一个 style.innerHTML 连续调用三次 toMatchSnapshot(),语义上是完全重复的:

expect(style.innerHTML).toMatchSnapshot();
expect(style.innerHTML).toMatchSnapshot();
expect(style.innerHTML).toMatchSnapshot();

保留一次即可,不会影响覆盖度,还能减少不必要的 snapshot 更新噪音。

src/hooks/useCacheToken.tsx (1)

179-213: hashId/hashCls 只依赖 salt + cssVar.prefix 的设计与测试契合,建议在代码里写明意图

cacheFn 中:

  • 使用 mergedSalt = \${salt}_${cssVar.prefix}`生成hashIdhashCls`;
  • 实际 token 内容变化并不会影响 hashId/hashCls,而是体现在 _tokenKeycssVarsStr 中。

结合 tests/index.spec.tsx 里 “hash only changes if salt or cssVar prefix changes” 的用例,这个设计是有意把「组件 class hash」与「主题版本(salt+prefix)」绑定,而不是与具体 token 值绑定。

实现没问题,但建议在这里加一两行注释,例如:

// hashId/hashCls 只依赖 salt + cssVar.prefix,保证 token 变更时 className 稳定,
// 仅当主题版本发生变化时才生成新的样式块。

能显著降低未来 refactor 时误改这块逻辑的概率。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7fd8a3 and a4da2e9.

⛔ Files ignored due to path filters (3)
  • tests/__snapshots__/css-variables.spec.tsx.snap is excluded by !**/*.snap
  • tests/__snapshots__/index.spec.tsx.snap is excluded by !**/*.snap
  • tests/__snapshots__/server.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (34)
  • docs/demo/auto-clear.md (0 hunks)
  • docs/demo/first-render.md (1 hunks)
  • docs/examples/auto-clear.tsx (0 hunks)
  • docs/examples/autoPrefix.tsx (1 hunks)
  • docs/examples/components/Button.tsx (7 hunks)
  • docs/examples/components/Spin.tsx (1 hunks)
  • docs/examples/components/theme.tsx (2 hunks)
  • docs/examples/css-var.tsx (2 hunks)
  • docs/examples/first-render.tsx (1 hunks)
  • package.json (3 hunks)
  • src/Cache.ts (3 hunks)
  • src/StyleContext.tsx (3 hunks)
  • src/extractStyle.ts (3 hunks)
  • src/hooks/useCSSVarRegister.ts (2 hunks)
  • src/hooks/useCacheToken.tsx (6 hunks)
  • src/hooks/useCompatibleInsertionEffect.tsx (0 hunks)
  • src/hooks/useEffectCleanupRegister.ts (2 hunks)
  • src/hooks/useGlobalCache.tsx (2 hunks)
  • src/hooks/useStyleRegister.tsx (6 hunks)
  • src/index.ts (2 hunks)
  • src/linters/contentQuotesLinter.ts (1 hunks)
  • src/transformers/autoPrefix.ts (1 hunks)
  • src/util/css-variables.ts (3 hunks)
  • src/util/index.ts (2 hunks)
  • tests/animation.spec.tsx (11 hunks)
  • tests/autoPrefix.spec.tsx (1 hunks)
  • tests/css-variables.spec.tsx (7 hunks)
  • tests/index.spec.tsx (34 hunks)
  • tests/keyframes.spec.tsx (1 hunks)
  • tests/legacy.spec.tsx (0 hunks)
  • tests/linter.spec.tsx (16 hunks)
  • tests/server.spec.tsx (12 hunks)
  • tests/theme.spec.tsx (2 hunks)
  • tests/util.spec.tsx (3 hunks)
💤 Files with no reviewable changes (4)
  • docs/demo/auto-clear.md
  • src/hooks/useCompatibleInsertionEffect.tsx
  • docs/examples/auto-clear.tsx
  • tests/legacy.spec.tsx
🧰 Additional context used
🧬 Code graph analysis (21)
tests/autoPrefix.spec.tsx (2)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
src/StyleContext.tsx (2)
  • StyleProvider (100-139)
  • createCache (16-52)
docs/examples/autoPrefix.tsx (2)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
src/StyleContext.tsx (1)
  • StyleProvider (100-139)
tests/keyframes.spec.tsx (1)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
docs/examples/first-render.tsx (1)
docs/examples/ssr-advanced.tsx (1)
  • Demo (9-43)
docs/examples/components/Spin.tsx (1)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
tests/util.spec.tsx (1)
src/hooks/useStyleRegister.tsx (1)
  • normalizeStyle (83-89)
tests/animation.spec.tsx (2)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/StyleContext.tsx (2)
  • StyleProvider (100-139)
  • createCache (16-52)
src/transformers/autoPrefix.ts (1)
src/index.ts (1)
  • Transformer (59-59)
docs/examples/components/Button.tsx (2)
docs/examples/components/theme.tsx (2)
  • DerivativeToken (23-25)
  • useToken (95-128)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
src/util/index.ts (1)
src/StyleContext.tsx (1)
  • HashPriority (54-54)
tests/theme.spec.tsx (1)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
tests/server.spec.tsx (5)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
src/util/cacheMapUtil.ts (2)
  • reset (27-30)
  • getStyleAndHash (71-95)
src/extractStyle.ts (1)
  • extractStyle (32-111)
src/StyleContext.tsx (2)
  • createCache (16-52)
  • StyleProvider (100-139)
docs/examples/css-var.tsx (2)
docs/examples/ssr-advanced.tsx (1)
  • Demo (9-43)
docs/examples/components/theme.tsx (1)
  • DesignTokenProvider (64-93)
src/hooks/useStyleRegister.tsx (4)
src/util/cacheMapUtil.ts (3)
  • existPath (65-69)
  • getStyleAndHash (71-95)
  • CSS_FILE_STYLE (10-10)
src/hooks/useGlobalCache.tsx (2)
  • useGlobalCache (18-106)
  • ExtractStyle (7-14)
src/util/index.ts (1)
  • toStyleStr (165-190)
src/StyleContext.tsx (3)
  • ATTR_MARK (10-10)
  • CSS_IN_JS_INSTANCE (14-14)
  • ATTR_CACHE_PATH (11-11)
src/hooks/useCacheToken.tsx (4)
src/util/index.ts (2)
  • flattenToken (39-63)
  • token2key (68-70)
src/hooks/useGlobalCache.tsx (1)
  • useGlobalCache (18-106)
src/util/css-variables.ts (1)
  • transformToken (39-94)
src/StyleContext.tsx (3)
  • ATTR_MARK (10-10)
  • CSS_IN_JS_INSTANCE (14-14)
  • ATTR_TOKEN (9-9)
tests/index.spec.tsx (3)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/StyleContext.tsx (3)
  • StyleProvider (100-139)
  • createCache (16-52)
  • ATTR_MARK (10-10)
src/hooks/useStyleRegister.tsx (2)
  • CSSInterpolation (67-70)
  • useStyleRegister (375-520)
src/StyleContext.tsx (1)
src/transformers/autoPrefix.ts (1)
  • AUTO_PREFIX (3-3)
docs/examples/components/theme.tsx (2)
tests/css-variables.spec.tsx (2)
  • DesignToken (18-30)
  • DerivativeToken (32-34)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/util/css-variables.ts (2)
src/StyleContext.tsx (1)
  • HashPriority (54-54)
src/util/index.ts (1)
  • where (192-202)
tests/linter.spec.tsx (2)
src/hooks/useCacheToken.tsx (1)
  • useCacheToken (152-237)
src/hooks/useStyleRegister.tsx (1)
  • useStyleRegister (375-520)
tests/css-variables.spec.tsx (2)
docs/examples/components/theme.tsx (1)
  • DesignTokenContext (54-62)
src/StyleContext.tsx (1)
  • StyleProvider (100-139)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: preview
🔇 Additional comments (29)
src/linters/contentQuotesLinter.ts (1)

14-14: LGTM!正确支持了 CSS 变量。

这个改动合理地允许 var() 函数在 content 属性中使用而不触发引号警告,与 PR 中引入的 CSS 变量支持功能一致。

src/util/index.ts (1)

192-202: LGTM!选择器生成逻辑正确。

where 函数根据 hashPriority 正确生成 CSS 选择器:

  • low 优先级(默认)使用 :where() 包裹以降低选择器优先级
  • low 优先级直接返回类选择器
  • 缺少 hashCls 时返回空字符串作为安全回退

这与 PR 中引入的哈希优先级特性保持一致。

src/StyleContext.tsx (2)

83-91: LGTM!设计合理。

autoPrefix 作为内部派生属性而非直接传入的 prop 是合理的设计。通过检测 transformers 数组中是否包含 AUTO_PREFIX 来自动设置该标志,避免了用户手动同步配置的负担。


120-127: 逻辑正确且清晰。

通过检查 transformers 中是否包含 AUTO_PREFIX 来自动设置 mergedContext.autoPrefix,这种方式确保了配置的一致性。

package.json (2)

3-3: 版本号提升至 2.0.0-alpha.9 符合 PR 目标。

该版本包含破坏性更改(移除 React < v18 兼容性、移除 style tokenKey 等),使用 alpha 版本号是合适的。


55-72: 依赖更新的配置兼容性已验证,但建议运行完整测试。

基于静态分析的验证结果:

  • ESLint 8.57.1:.eslintrc.js 正确使用 @umijs/fabric/dist/eslint 配置,结构符合 v8 标准
  • @testing-library/react 14.0.0:与 React 18 兼容(v13 起支持,v14 延续),项目 peerDependencies react >=16.0.0 满足
  • @umijs/fabric 4.0.1:v4 配置方式与项目现状一致,无已知 ESLint 8 不兼容问题
  • React 版本:peerDependencies 要求 >=16.0.0,与 React 18 兼容

虽然配置文件结构正确,但未实际执行 npm run lintnpm run test 来确认运行时兼容性。建议在合并前运行这两个命令以确保无隐藏的 schema 验证错误或规则冲突。

docs/demo/first-render.md (1)

1-8: LGTM!文档结构正确。

新增的 First Render 演示文档结构清晰,正确引用了示例代码文件。

docs/examples/components/Spin.tsx (1)

44-51: LGTM!简化了组件渲染逻辑。

移除了 SSR 包装器,现在 useStyleRegister 纯粹用于副作用(样式注册),组件直接渲染 div。这与仓库中更新的样式注册架构保持一致。

src/index.ts (1)

19-19: LGTM!正确暴露了新的 transformer。

autoPrefixTransformer 的导入和导出遵循现有 transformer 的模式,为用户提供了自动添加浏览器前缀的能力。

Also applies to: 39-39

src/Cache.ts (1)

23-24: updateTimes 映射的实现逻辑正确

updateTimes 映射与 cache 的同步管理是一致的:删除时清理两者,更新时同步记录。实现逻辑正确。

Also applies to: 54-54, 57-58

src/hooks/useCSSVarRegister.ts (1)

33-33: hashId 集成逻辑清晰

hashId 作为可选参数添加,并正确地传递给 transformToken 作为 hashCls。与上下文中的 hashPriority 配合使用,集成逻辑清晰一致。

Also applies to: 37-37, 41-41, 57-58

docs/examples/autoPrefix.tsx (1)

1-51: 示例代码清晰展示了 autoPrefix 功能

这个示例很好地演示了如何使用 autoPrefixTransformer,包含了需要浏览器前缀的典型属性(transform、backdropFilter、userSelect)。代码结构清晰,易于理解。

tests/keyframes.spec.tsx (1)

39-39: 测试已正确更新以使用新的 useCacheToken API

测试代码已更新为传递 options 对象参数(包含 cssVar 配置),符合新的 API 签名要求。

src/extractStyle.ts (1)

63-63: 基于时间的样式排序逻辑实现正确

通过引入 updateTime 实现了二级排序:当 order 相同时,按 updateTime 排序。这确保了相同优先级的样式有确定性的顺序。实现逻辑正确,类型安全。

Also applies to: 77-77, 86-86, 89-94

tests/util.spec.tsx (1)

43-43: 测试已正确更新以使用新的 normalizeStyle 签名

所有 normalizeStyle 调用都已更新为传递第二个参数(autoPrefix),这些测试场景传递 false 以测试非前缀化行为。更新正确。

Also applies to: 99-99, 160-160

tests/theme.spec.tsx (1)

115-115: 测试正确适配了新的 useCacheToken 返回值结构

测试代码已更新为解构第三个元素(actualToken)并传递必需的 options 对象。这与新的 API 签名一致,实际 token 现在位于返回元组的第三个位置。

Also applies to: 117-117, 161-161, 163-163

tests/autoPrefix.spec.tsx (1)

12-79: autoPrefix 测试覆盖合理

测试用例覆盖了启用和未启用 autoPrefixTransformer 两种场景,结构清晰,断言合理。能够有效验证自动前缀功能的正确性。

docs/examples/css-var.tsx (1)

4-54: 示例用法与主题切换逻辑看起来都没问题

  • 使用 DesignTokenProvider 包裹 Demo 展示不同 primaryColor 的效果很清晰。
  • 通过本地 color 状态和 “Change theme” 按钮切换主题实现也合理,没有发现明显逻辑问题。
docs/examples/first-render.tsx (1)

1-49: 渲染耗时示例实现正确,行为符合预期

  • 使用 useRef 记录挂载时间、useEffect([]) 计算首屏渲染耗时的方式合理。
  • 通过 checkbox 控制是否渲染 10000 项,清晰地表达了性能测试场景。示例本身没有明显问题,仅需注意实际运行时渲染会比较重。
tests/linter.spec.tsx (1)

17-351: linter 测试用例与新 cssVar / contentQuotes 行为完全对齐,无需调整

验证结果确认:

  • rc-util 的 resetWarned() 函数用于清空内部记录的"已提示过"的 warning,从而允许同一条 warning 再次输出,与测试中的使用意图完全一致。
  • contentQuotesLinter 描述块的 beforeAll 中调用 resetWarned() 可正确地清除去重状态,避免之前的 warning 状态干扰本组测试断言。
  • 所有 useCacheToken 调用统一补充 { cssVar: { key: 'css-var-test' } } 选项,符合新实现设计。
  • errorSpyafterEach.mockResetafterAll.mockRestore 生命周期管理清晰,不会污染其他测试。
  • contentQuotesLinter 的两条测试分别覆盖 content: 'var(--content-value)'(不告警)和 content: 'test'(告警)两个主要分支,测试覆盖面完整。
tests/server.spec.tsx (3)

217-249: default hashPriority 用例看起来合理,可以继续保持快照覆盖

MyBox 里为 useCacheToken 补上了 cssVar 配置,并在 StyleProvider 上设置 hashPriority="high",再通过快照验证生成样式。整体逻辑与实际实现(hashPriority 影响 cssVar 和样式注入顺序)一致,测试能覆盖该路径,没看到明显问题。


365-433: 新增加的 extract with order 辅助测试整体设计清晰

extract with order 通过 testOrder 抽象出不同组件渲染顺序与最终纯样式串中 .a/.b/.c 出现顺序的关系,覆盖了多种排列组合,能够很好验证样式插入顺序的稳定性。实现上利用 indexOf 和递增比较,简单直接,足够支撑测试目的。后续如果还要扩展更多组件,最多只是增加数组长度和参数类型注解,当前结构已经比较易维护。


448-462: once 选项行为测试覆盖到位

should extract once when once option is true 用例很好地验证了 extractStyleonce 语义:第一次只抽取已有的 .box 样式,第二次在同一 cache 基础上只抽取新增的 .card。这和 cache.extracted 的预期行为一致,看不出问题。

tests/css-variables.spec.tsx (1)

333-380: 前缀切换 + hashed 用例能很好地锁定 cssVar 再生成行为

在 “css var prefix should regenerate component style” 中通过 hashed: true 配合不同的 prefixappbank),分别断言样式中是否出现对应的 var(--app- / var(--bank-),并进一步在第二次渲染后确认两种前缀共存。这很好地覆盖了 cssVar 前缀变化不会破坏旧样式、且会按需生成新样式的场景,逻辑清晰、断言也比较稳健。

tests/animation.spec.tsx (1)

182-188: re-mount 测试与 useGlobalCache 的微任务清理契合良好

re-mount should not missing animation style 改为 async 并在两次渲染之间插入 await Promise.resolve(),结合 document.head.innerHTML = '',显然是在验证当 DOM 被手动清空后,通过微任务清理 effectMap 仍能确保再次渲染时动画样式被重新注入。配合最新的 useGlobalCache 实现,这个测试能很好锁定这一行为,看起来是合理且必要的覆盖。

Also applies to: 209-215, 223-224

src/hooks/useStyleRegister.tsx (1)

7-8: normalizeStyle + autoPrefix 行为建议再整体验证一遍(尤其 SSR / 客户端一致性)

现在所有样式(主样式与 effectStyle,包括 @layer 依赖)都会通过 styliscompile/serialize 流程,再根据 autoPrefix 决定是否加 prefixer,同时 SSR extract 也会对 effectStyle 再跑一遍 normalizeStyle。整体思路是对的,但有几点值得特别确认一下:

  • autoPrefix=false 时依然走 stylis 管线,相比之前纯字符串拼接,可能对边缘语法(例如特殊占位符 {%%%:...;}、复杂嵌套、@layer Hack)有轻微行为差异,建议确保相应 case 已在测试中覆盖。
  • 客户端注入和 extract 中使用的 autoPrefix 需保持完全一致,否则存在少见但难查的「水合后 CSS 文本不完全一致」问题(目前看 wiring 是对的,但这块最好在服务器端样式快照测试里再确认一次)。
  • 如果后续在性能上遇到瓶颈,可以考虑仅在 autoPrefix 打开时才经由 stylis,关闭时走旧的轻量路径,不过这属于优化而非当前阻塞点。

整体实现看起来合理,只是这块属于较底层的行为变更,建议再跑一轮针对 @layerkeyframes、深度嵌套 selector 的回归。

Also applies to: 83-89, 434-445, 474-517, 552-558

tests/index.spec.tsx (2)

59-63: 针对新 cssVar 选项的测试覆盖度不错

这些用例系统性地把 useCacheToken 的第三个参数切换为 { cssVar: { key: 'css-var-test', ... } } 或带 prefix/hashed 的配置,覆盖了:

  • 基础组件场景(Box / Nest / OverBox 等)下 css 变量块 + 组件样式的插入与数量;
  • override、自定义 getComputedToken、多值属性、undefined 值等边缘情况;
  • token 嵌套、getComputedToken 多主题合并、cssVar.key 变化对 hash 和 DOM 的影响;
  • React 18 / StrictMode 场景中,cleanup 顺序与 warning 行为。

整体来看,这一批断言基本把新 API 的核心行为都跑过了一遍,能很好地为底层实现(useCacheToken / useStyleRegister / css 变量序列化)保驾护航。

Also applies to: 176-178, 202-215, 231-235, 275-277, 325-332, 387-393, 447-453, 519-525, 571-574, 599-602, 630-635, 718-724, 790-796, 851-854


874-950: “hash 只随 salt 和 cssVar.prefix 变化”的用例设计很好

新增的 it('hash only changes if salt or cssVar prefix changes', ...) 用例清晰验证了:

  • saltcssVar.prefix 不变的情况下,即便 token 内容变化,组件样式 hash 仍保持稳定;
  • 只有当 saltprefix 变化时,才会产生新的组件样式标签,而 css 变量块保持不变。

这很好地锁定了新 hash 策略的预期行为,有利于防止后续 refactor 不小心把 hash 再次绑回 token 内容本身。

src/hooks/useCacheToken.tsx (1)

185-195: css 变量样式插入与 SSR extract 的契合性

几个细节看起来是合理的,但值得在这里总结一下,方便后续维护者理解这一段复杂流程:

  • actualToken._tokenKey = token2key(actualToken, mergedSalt) 之后,在 extract 中用 _tokenKey 作为 cssVar <style>ATTR_MARK,而运行时插入 cssVar 样式使用的是 hash('css-var-' + themeKey) 作为 hash。两者 hash 来源不同,但:
    • SSR 侧只负责输出 <style data-token-hash=cssVarKey data-css-hash=_tokenKey>...;
    • 客户端重用 cssVar 样式只依赖 ATTR_TOKENCSS_IN_JS_INSTANCE,与 SSR 侧的 _tokenKey 不耦合。
  • onCacheEffect 里对 cssVarsStr 判空再调用 updateCSS,可以避免空变量块插入,随后通过 ATTR_TOKEN = themeKey + CSS_IN_JS_INSTANCEcleanTokenStyle 能够按 themeKey + 实例维度批量删除。
  • extract 返回的 [order, styleId, styleText] 固定 order = -999,与运行时 priority: -999 保持一致,有利于 rc-util 侧按优先级统一处理。

整体上 SSR / 客户端 两侧在 css 变量块的顺序与优先级上是一致的,实现 LGTM,只是这块逻辑比较绕,建议未来如果再调整 hash 或清理策略时把这几个约束一并考虑。

Also applies to: 197-213, 219-233, 239-270

@MadCcc MadCcc merged commit c06da68 into master Nov 17, 2025
9 checks passed
@MadCcc MadCcc deleted the chore/merge-next branch November 17, 2025 09:43
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.

4 participants