-
Notifications
You must be signed in to change notification settings - Fork 80
chore: master merge next #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* 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: 更新测试用例
|
🎊 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 |
|
Note Other AI code review bot(s) detectedCodeRabbit 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。此外还更新了文档示例和相应的测试。 变更
序列图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
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 变量
代码审查工作量估计🎯 4 (复杂) | ⏱️ ~60 分钟 需要重点关注的文件:
可能相关的 PR
诗
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello @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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this 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 = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } | ||
|
|
||
| /** Record update id for extract static style order. */ | ||
| let updateId = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this 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 个按钮),这可能会导致:
- 浏览器性能问题
- 测试运行缓慢
- 用户体验不佳
如果这是为了性能基准测试,建议:
- 添加注释说明为什么需要这么多组件
- 考虑将数量设为可配置的
+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
⛔ Files ignored due to path filters (3)
tests/__snapshots__/css-variables.spec.tsx.snapis excluded by!**/*.snaptests/__snapshots__/index.spec.tsx.snapis excluded by!**/*.snaptests/__snapshots__/server.spec.tsx.snapis 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。新的转换器已正确导入并导出,与现有的其他转换器(如
legacyLogicalPropertiesTransformer和px2remTransformer)保持一致的结构。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选项以匹配更新后的useCacheTokenAPI。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参数以及将hashPriority和hashCls传递给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.key和actualToken为使用者提供了更大的灵活性。这允许组件同时访问 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 值。
| 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> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.json 的 peerDependencies 仍声明为 >=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:77 和 tests/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.
| import type { Transformer } from './interface'; | ||
|
|
||
| export const AUTO_PREFIX = {}; | ||
|
|
||
| const transform: Transformer = AUTO_PREFIX; | ||
|
|
||
| export default transform; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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_PREFIX在StyleContext.tsx第 125 行被用作哨兵值检测:当transformers数组中包含AUTO_PREFIX时,设置autoPrefix布尔标志为true- 实际的 CSS 自动前缀添加由 @ant-design/cssinjs 中的 stylis 库通过
normalizeStyle函数完成,而非通过Transformer.visit()方法 - 与其他转换器(如
legacyLogicalProperties、px2rem)不同,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.
| 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'); | ||
| }); |
There was a problem hiding this comment.
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.
| 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
移除调试用的 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.
| 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.
tests/server.spec.tsx
Outdated
| 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>; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
移除重复定义以恢复编译
Line 78-102 新增了一套 genCardStyle 与 Card 定义,但原有定义仍然保留,导致同一作用域内重复声明,正如流水线报错 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.
| 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.
There was a problem hiding this 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配合useInsertionEffect和Promise.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 执行清理。这有两个点值得注意:
- 这段逻辑的设计目的(例如为了配合 React 18 双渲染 / remount 行为延后或提前清理)从代码本身并不直观,后续维护者很难一眼看出为什么是
-1,建议在TOKEN_THRESHOLD和cleanTokenStyle附近补一个简短注释说明设计意图和期望场景。tokenKeys以cssVar.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 可以考虑抽取工具函数这段对
useCacheToken和useStyleRegister的改动与当前 cssVar API 一致,行为上看没有问题。不过{ cssVar: { key: 'css-var-test' } }在 Box、Card 以及后面几个测试组件里多次重复出现,后续如果 key 或配置变动,容易漏改。可以考虑在文件顶部抽一个小的const cssVarOption = { cssVar: { key: 'css-var-test' } }复用,简化测试代码。
124-126: SSR 断言与内部实现耦合稍紧,可适当放宽匹配条件这里对
html、style和plainStyle采用快照断言,同时对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: false和css-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重命名为更贴切的themeKey或className;- 或者在函数上方补一行注释,说明第二个参数含义是「cssVar 容器 class」,而非散列值。
当前实现本身是正确的,只是命名容易让人联想到
ATTR_MARK的 hash。Also applies to: 12-22, 25-30
39-60: transformToken 的 hashCls/hashPriority 透传设计合理,建议在注释中强调用途
transformToken现在把hashCls和hashPriority一路传到serializeCSSVar并利用where({ hashCls, hashPriority })包裹变量块,从而实现:
- 当
cssVar.hashed为真时,变量块受 hash class 作用域控制(低优先级时通过:where()包裹);- 当
hashed关闭时,回退到没有 hash class 的.themeKey{--var:...}。逻辑上是合理的,不过这两个选项的意图(特别是「为什么变量块需要 hash 作用域」)对后来者不算直观,建议在
transformToken或css-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}`生成hashId和hashCls`;- 实际 token 内容变化并不会影响
hashId/hashCls,而是体现在_tokenKey和cssVarsStr中。结合
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
⛔ Files ignored due to path filters (3)
tests/__snapshots__/css-variables.spec.tsx.snapis excluded by!**/*.snaptests/__snapshots__/index.spec.tsx.snapis excluded by!**/*.snaptests/__snapshots__/server.spec.tsx.snapis 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 lint和npm 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' } }选项,符合新实现设计。errorSpy的afterEach.mockReset和afterAll.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用例很好地验证了extractStyle的once语义:第一次只抽取已有的.box样式,第二次在同一cache基础上只抽取新增的.card。这和cache.extracted的预期行为一致,看不出问题。tests/css-variables.spec.tsx (1)
333-380: 前缀切换 + hashed 用例能很好地锁定 cssVar 再生成行为在 “css var prefix should regenerate component style” 中通过
hashed: true配合不同的prefix(app→bank),分别断言样式中是否出现对应的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依赖)都会通过stylis的compile/serialize流程,再根据autoPrefix决定是否加prefixer,同时 SSRextract也会对 effectStyle 再跑一遍normalizeStyle。整体思路是对的,但有几点值得特别确认一下:
autoPrefix=false时依然走 stylis 管线,相比之前纯字符串拼接,可能对边缘语法(例如特殊占位符{%%%:...;}、复杂嵌套、@layerHack)有轻微行为差异,建议确保相应 case 已在测试中覆盖。- 客户端注入和
extract中使用的autoPrefix需保持完全一致,否则存在少见但难查的「水合后 CSS 文本不完全一致」问题(目前看 wiring 是对的,但这块最好在服务器端样式快照测试里再确认一次)。- 如果后续在性能上遇到瓶颈,可以考虑仅在
autoPrefix打开时才经由 stylis,关闭时走旧的轻量路径,不过这属于优化而非当前阻塞点。整体实现看起来合理,只是这块属于较底层的行为变更,建议再跑一轮针对
@layer、keyframes、深度嵌套 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', ...)用例清晰验证了:
- 在
salt和cssVar.prefix不变的情况下,即便 token 内容变化,组件样式 hash 仍保持稳定;- 只有当
salt或prefix变化时,才会产生新的组件样式标签,而 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_TOKEN和CSS_IN_JS_INSTANCE,与 SSR 侧的_tokenKey不耦合。onCacheEffect里对cssVarsStr判空再调用updateCSS,可以避免空变量块插入,随后通过ATTR_TOKEN = themeKey+CSS_IN_JS_INSTANCE让cleanTokenStyle能够按 themeKey + 实例维度批量删除。extract返回的[order, styleId, styleText]固定order = -999,与运行时priority: -999保持一致,有利于 rc-util 侧按优先级统一处理。整体上 SSR / 客户端 两侧在 css 变量块的顺序与优先级上是一致的,实现 LGTM,只是这块逻辑比较绕,建议未来如果再调整 hash 或清理策略时把这几个约束一并考虑。
Also applies to: 197-213, 219-233, 239-270

Summary by CodeRabbit
新功能
Bug修复
var()函数文档
重构
Chores