Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 42 additions & 11 deletions src/StyleContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@

export type HashPriority = 'low' | 'high';

export interface StyleContextProps {
export type LayerComposer = (dependency: ReadonlySet<string>) => string;
Copy link
Member

Choose a reason for hiding this comment

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

太复杂了,我们修复的话应该是让用户最简单的心智去处理。理论上来说,他们只要知道自己的上游依赖是什么就行了,而不用关心整体如何聚合出来。

Copy link
Member Author

Choose a reason for hiding this comment

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

嗯嗯 get

export type LayerConfig = {
/** Define the hierarchical order here */
composer: LayerComposer;
};
Comment on lines +55 to +59
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

类型命名冲突需要解决

src/hooks/useStyleRegister.tsx 中存在另一个同名的 LayerConfig 接口,但具有不同的属性结构(name: string; dependencies?: string[]),这可能导致类型冲突和混淆。

建议重命名其中一个接口以避免冲突:

-export type LayerConfig = {
+export type StyleLayerConfig = {
  /** Define the hierarchical order here */
  composer: LayerComposer;
};

并相应更新所有引用。


🏁 Script executed:

#!/bin/bash
# 验证是否存在 LayerConfig 类型冲突
rg -n "LayerConfig" --type ts

Length of output: 372


修复:解决 LayerConfig 类型命名冲突

src/StyleContext.tsxsrc/hooks/useStyleRegister.tsx 中都定义了同名的 LayerConfig,虽然位于不同模块,但在引入或统一导出时极易混淆,建议重命名一侧以避免歧义。

请在 src/StyleContext.tsx 中将 LayerConfig 重命名为 StyleLayerConfig 并更新所有引用:

  • src/StyleContext.tsx:56
  • src/StyleContext.tsx:90
  • src/StyleContext.tsx:106

示例修改:

--- a/src/StyleContext.tsx
+++ b/src/StyleContext.tsx
@@ -53,7 +53,7 @@ export type LayerComposer = (dependency: ReadonlySet<string>) => string;

-export type LayerConfig = {
+export type StyleLayerConfig = {
  /** Define the hierarchical order here */
  composer: LayerComposer;
};

@@ -87,7 +87,7 @@ export interface StyleProviderProps {
   /** 启用层级模式时的配置 */
-  layer?: LayerConfig;
+  layer?: StyleLayerConfig;
 }

 export const StyleProvider: React.FC<StyleProviderProps> = ({
@@ -103,7 +103,7 @@ export const StyleProvider: React.FC<StyleProviderProps> = ({

   /** 如果传入布尔值,则使用默认 composer;若传入对象则按定义解析 */
-  layer?: boolean | LayerConfig;
+  layer?: boolean | StyleLayerConfig;

   children,
 }) => {

完成重命名后,请确认相关导出和调用处(如统一导出的 index.ts)已同步更新,确保无遗漏。

🤖 Prompt for AI Agents
In src/StyleContext.tsx around lines 55 to 59, rename the LayerConfig type to
StyleLayerConfig to avoid naming conflicts with another LayerConfig in
src/hooks/useStyleRegister.tsx. Update all references to LayerConfig within
src/StyleContext.tsx, including lines 56, 90, and 106, to use StyleLayerConfig.
Also, ensure that any exports or imports related to this type, such as in
index.ts, are updated accordingly to maintain consistency and prevent confusion.


export interface StyleContextValue {
autoClear?: boolean;
/** @private Test only. Not work in production. */
mock?: 'server' | 'client';
Expand All @@ -77,38 +83,63 @@
* Please note that `linters` do not support dynamic update.
*/
linters?: Linter[];
/** Wrap css in a layer to avoid global style conflict */
layer?: boolean;
/**
* Wrap css in a layer to avoid global style conflict
* @see [MDN-CSS Layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)
*/
layer?: LayerConfig;
}

const StyleContext = React.createContext<StyleContextProps>({
export const defaultLayerComposer: LayerComposer = (dependency) =>
Array.from(dependency).join();

const noop = () => ``;

const StyleContext = React.createContext<StyleContextValue>({
hashPriority: 'low',
cache: createCache(),
defaultCache: true,
});

export type StyleProviderProps = Partial<StyleContextProps> & {
children?: React.ReactNode;
};
export interface StyleProviderProps
extends Omit<Partial<StyleContextValue>, 'layer'> {
layer?: boolean | LayerConfig;
}

export const StyleProvider: React.FC<StyleProviderProps> = (props) => {
export const StyleProvider = (
props: React.PropsWithChildren<StyleProviderProps>,
) => {
const { children, ...restProps } = props;

const parentContext = React.useContext(StyleContext);

const context = useMemo<StyleContextProps>(
const context = useMemo<StyleContextValue>(
() => {
const mergedContext: StyleContextProps = {
const mergedContext: StyleContextValue = {
...parentContext,
};

(Object.keys(restProps) as (keyof StyleContextProps)[]).forEach((key) => {
(Object.keys(restProps) as (keyof StyleContextValue)[]).forEach((key) => {
const value = restProps[key];
if (restProps[key] !== undefined) {
(mergedContext as any)[key] = value;
}
});

// Standardize layer
const { layer } = mergedContext;
if (typeof layer === 'boolean') {
mergedContext.layer = layer
? { composer: defaultLayerComposer }
: { composer: noop };

Check warning on line 134 in src/StyleContext.tsx

View check run for this annotation

Codecov / codecov/patch

src/StyleContext.tsx#L134

Added line #L134 was not covered by tests
} else if (typeof layer === 'object') {
mergedContext.layer = {
...layer,
// Ensure composer is always a function
composer: layer.composer ?? defaultLayerComposer,
};
}

const { cache } = restProps;
mergedContext.cache = mergedContext.cache || createCache();
mergedContext.defaultCache = !cache && parentContext.defaultCache;
Expand Down
28 changes: 19 additions & 9 deletions src/hooks/useStyleRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { Theme, Transformer } from '..';
import type Keyframes from '../Keyframes';
import type { Linter } from '../linters';
import { contentQuotesLinter, hashedAnimationLinter } from '../linters';
import type { HashPriority } from '../StyleContext';
import type { HashPriority, LayerComposer } from '../StyleContext';
import StyleContext, {
ATTR_CACHE_PATH,
ATTR_MARK,
Expand Down Expand Up @@ -128,7 +128,9 @@ function injectSelectorHash(
export interface ParseConfig {
hashId?: string;
hashPriority?: HashPriority;
layer?: LayerConfig;
layer?: LayerConfig & {
composer?: LayerComposer;
};
path?: string;
transformers?: Transformer[];
linters?: Linter[];
Expand Down Expand Up @@ -333,15 +335,17 @@ export const parseStyle = (
if (!root) {
styleStr = `{${styleStr}}`;
} else if (layer) {
const { name, dependencies, composer } = layer;
// fixme: https://github.com/thysultan/stylis/pull/339
if (styleStr) {
styleStr = `@layer ${layer.name} {${styleStr}}`;
styleStr = `@layer ${name} {${styleStr}}`;
}

if (layer.dependencies) {
effectStyle[`@layer ${layer.name}`] = layer.dependencies
.map((deps) => `@layer ${deps}, ${layer.name};`)
.join('\n');
if (dependencies) {
const dependency = new Set([...dependencies, name]);
const combinedDependencies =
composer?.(dependency) ?? Array.from(dependency).join(', ');
effectStyle[`@layer ${name}`] = `@layer ${combinedDependencies};`;
}
}

Expand Down Expand Up @@ -402,9 +406,10 @@ export default function useStyleRegister(
transformers,
linters,
cache,
layer: enableLayer,
layer: ctxLayer,
} = React.useContext(StyleContext);
const tokenKey = token._tokenKey as string;
Copy link
Preview

Copilot AI Jun 4, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider adding an inline comment to explain the rationale behind checking if 'composer' exists in ctxLayer to determine enableLayer. This will improve code clarity for future maintainers.

Suggested change
const tokenKey = token._tokenKey as string;
const tokenKey = token._tokenKey as string;
// Check if 'composer' exists in ctxLayer to determine if layer functionality is enabled.

Copilot uses AI. Check for mistakes.

const enableLayer = 'composer' in (ctxLayer || {});

const fullPath = [tokenKey];
if (enableLayer) {
Expand Down Expand Up @@ -446,7 +451,12 @@ export default function useStyleRegister(
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId,
hashPriority,
layer: enableLayer ? layer : undefined,
layer: enableLayer
? {
...layer!,
composer: ctxLayer!.composer,
}
: undefined,
path: path.join('-'),
transformers,
linters,
Expand Down
41 changes: 41 additions & 0 deletions tests/layer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,45 @@ describe('layer', () => {
const styles = Array.from(document.head.querySelectorAll('style'));
expect(styles[0].innerHTML.trim()).toEqual('');
});

// https://github.com/ant-design/pro-components/issues/8955
it('custom layer composer', () => {
const theme = createTheme(() => ({}));
const Demo = () => {
useStyleRegister(
{
theme,
token: { _tokenKey: 'test' },
path: ['shared'],
layer: {
name: 'pro',
dependencies: ['basic'],
},
},
() => ({
p: {
color: 'red',
},
}),
);
return null;
};

render(
<StyleProvider
layer={{
composer: (deps) =>
['tw-base', ...Array.from(deps), 'tw-utils'].join(', '),
}}
cache={createCache()}
>
<Demo />
</StyleProvider>,
);

const styles = Array.from(document.head.querySelectorAll('style'));
expect(styles[0].innerHTML.trim()).toEqual(
'@layer tw-base,basic,pro,tw-utils;',
);
});
});