Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion packages/x/components/theme/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ export interface DesignTokenProviderProps {
*);
* ```
*/
// zeroRuntime?: boolean;
zeroRuntime?: boolean;
}
4 changes: 2 additions & 2 deletions packages/x/components/theme/genStyleUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export const { genStyleHooks, genComponentStyleHook, genSubStyleComponent } = ge
};
},
useToken: () => {
const [theme, realToken, hashId, token, cssVar] = useInternalToken();
return { theme, realToken, hashId, token, cssVar };
const [theme, realToken, hashId, token, cssVar, zeroRuntime] = useInternalToken();
return { theme, realToken, hashId, token, cssVar, zeroRuntime };
},
useCSP: () => {
const { csp } = useXProviderContext();
Expand Down
11 changes: 10 additions & 1 deletion packages/x/components/theme/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@ export function useInternalToken(): [
hashId: string,
realToken: GlobalToken,
cssVar?: DesignTokenProviderProps['cssVar'],
zeroRuntime?: boolean,
] {
const {
token: rootDesignToken,
hashed,
theme,
override,
cssVar: ctxCssVar,
zeroRuntime,
} = React.useContext(antdTheme._internalContext);
Comment on lines 109 to 116
Copy link
Contributor

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

🏁 Script executed:

#!/bin/bash
# First, check for zeroRuntime references in the codebase
echo "=== Searching for zeroRuntime ==="
rg "zeroRuntime" --type=ts --type=tsx -n

echo -e "\n=== Checking antd version in dependencies ==="
cat package.json | grep -A 5 -B 5 "antd" | head -20

echo -e "\n=== Searching for _internalContext ==="
rg "_internalContext" --type=ts --type=tsx -n | head -30

echo -e "\n=== Looking for DesignTokenContext or similar type defs ==="
fd -e "d.ts" -o -e "tsx" -o -e "ts" | xargs grep -l "_internalContext\|DesignTokenContext" 2>/dev/null | head -10

Repository: ant-design/x

Length of output: 978


🏁 Script executed:

#!/bin/bash
# Find and read the actual file mentioned
echo "=== Reading the target file ==="
if [ -f "packages/x/components/theme/useToken.ts" ]; then
  echo "File found. Reading lines 100-120:"
  sed -n '100,120p' "packages/x/components/theme/useToken.ts" | cat -n
else
  echo "File not found at expected path"
  # Try to find it
  find . -name "useToken.ts" -type f 2>/dev/null
fi

echo -e "\n=== Searching for zeroRuntime in all file types ==="
rg "zeroRuntime" -n 2>/dev/null | head -20

echo -e "\n=== Searching for _internalContext in all file types ==="
rg "_internalContext" -n 2>/dev/null | head -20

echo -e "\n=== Checking node_modules antd types ==="
if [ -d "node_modules/antd" ]; then
  find node_modules/antd -name "*.d.ts" -type f 2>/dev/null | xargs grep -l "_internalContext" 2>/dev/null | head -5
fi

Repository: ant-design/x

Length of output: 2457


🏁 Script executed:

#!/bin/bash
echo "=== Searching for _internalContext definition in antd types ==="
find node_modules/antd -type f \( -name "*.d.ts" -o -name "*.ts" \) 2>/dev/null | \
  xargs grep -l "_internalContext" 2>/dev/null

echo -e "\n=== Looking for DesignTokenContext or ThemeContextType definition ==="
find node_modules/antd -type f -name "*.d.ts" 2>/dev/null | \
  xargs grep -B 5 -A 15 "internalContext\|DesignTokenContext" 2>/dev/null | head -100

echo -e "\n=== Checking antd version and theme exports ==="
cat node_modules/antd/package.json | grep '"version"'

echo -e "\n=== Searching context.ts in antd to see context definition ==="
find node_modules/antd -name "context.ts" -o -name "context.tsx" 2>/dev/null | \
  xargs cat 2>/dev/null | head -150

Repository: ant-design/x

Length of output: 383


🏁 Script executed:

#!/bin/bash
echo "=== Finding where antdTheme is imported ==="
rg "import.*antd" packages/x/components/theme/useToken.ts -A 5 -B 5

echo -e "\n=== Checking imports at top of useToken.ts ==="
head -30 packages/x/components/theme/useToken.ts

echo -e "\n=== Looking for type definitions of _internalContext in packages/x ==="
find packages/x -name "*.d.ts" -o -name "*.ts" | xargs grep -l "_internalContext\|DesignTokenContext" 2>/dev/null

echo -e "\n=== Searching for antd import statement and usage ==="
rg "from 'antd'" packages/x -n | head -20

echo -e "\n=== Checking context.ts for context type definition ==="
cat -n packages/x/components/theme/context.ts

echo -e "\n=== Looking for any documentation or comments about zeroRuntime ==="
rg "zeroRuntime" -B 3 -A 3 packages/x/components/theme/ --type=ts

Repository: ant-design/x

Length of output: 7689


_internalContext 是 antd 的私有 API,但该属性在 v6.1.1 中已被使用并正式文档化

虽然 _internalContext 确实是 antd 的内部 API,但 zeroRuntime 属性已在 antd v6.1.1 中被定义且可访问。本项目中该功能经过充分测试(见 zeroRuntime.test.tsx)且在 JSDoc 中有文档记录(@since 2.0.0@default true),表明这是一个相对稳定的内部接口。

不过,由于依赖于私有 API,建议在升级 antd 主版本时重点检查 _internalContext 的结构变更。

🤖 Prompt for AI Agents
In `@packages/x/components/theme/useToken.ts` around lines 109 - 116, You're using
the private API antdTheme._internalContext to read zeroRuntime in useToken; make
this resilient by feature-detecting the context shape (check
antdTheme._internalContext exists and has token/zeroRuntime) and provide a safe
fallback (e.g., default zeroRuntime value) so upgrades won't break; update
useToken to guard reads of token/hashed/theme/override/cssVar/zeroRuntime, keep
the existing zeroRuntime.test.tsx and JSDoc (`@since` 2.0.0, `@default` true) to
document stability, and add a short comment noting to verify _internalContext
structure when bumping antd major.

const cssVar = {
prefix: ctxCssVar?.prefix ?? 'ant',
Expand All @@ -134,7 +136,14 @@ export function useInternalToken(): [
},
},
);
return [mergedTheme as Theme<any, any>, realToken, hashed ? hashId : '', token, cssVar];
return [
mergedTheme as Theme<any, any>,
realToken,
hashed ? hashId : '',
token,
cssVar,
!!zeroRuntime,
];
}

export default function useToken() {
Expand Down
80 changes: 80 additions & 0 deletions packages/x/components/x-provider/__tests__/zeroRuntime.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { createCache, StyleProvider } from '@ant-design/cssinjs';
import React from 'react';
import { render } from '../../../tests/utils';
import { Bubble, Sender } from '../../index';
import XProvider from '../index';

/**
* Helper to find style tags that contain actual component styles (layout, visual)
* as opposed to CSS variable declarations only.
*
* CSS variable styles only contain `--ant-*` custom property declarations.
* Component styles contain actual CSS properties like `display`, `position`, `padding`, etc.
*/
function findComponentStyles(selector: string): HTMLStyleElement | undefined {
const styleList = Array.from(document.head.querySelectorAll('style'));
return styleList.find((style) => {
const html = style.innerHTML;
if (!html.includes(selector)) return false;
// Strip all CSS variable declarations to check if real properties remain
const withoutVars = html.replace(/--[\w-]+:[^;]+;/g, '');
// If only variable declarations existed, stripping them leaves no property declarations
return /[\w-]+:/.test(withoutVars);
});
}

describe('XProvider.zeroRuntime', () => {
beforeEach(() => {
document.head.innerHTML = '';
});

it('should inject component styles at runtime by default', () => {
render(
<StyleProvider cache={createCache()}>
<XProvider>
<Bubble content="test" />
</XProvider>
</StyleProvider>,
);

expect(findComponentStyles('.ant-bubble')).toBeTruthy();
});

it('should skip component style injection when zeroRuntime is true', () => {
render(
<StyleProvider cache={createCache()}>
<XProvider theme={{ zeroRuntime: true }}>
<Bubble content="test" />
</XProvider>
</StyleProvider>,
);

expect(findComponentStyles('.ant-bubble')).toBeFalsy();
});

it('should skip component style injection for multiple components', () => {
render(
<StyleProvider cache={createCache()}>
<XProvider theme={{ zeroRuntime: true }}>
<Bubble content="test" />
<Sender />
</XProvider>
</StyleProvider>,
);

expect(findComponentStyles('.ant-bubble')).toBeFalsy();
expect(findComponentStyles('.ant-sender')).toBeFalsy();
});

it('should still render component DOM when zeroRuntime is true', () => {
const { container } = render(
<StyleProvider cache={createCache()}>
<XProvider theme={{ zeroRuntime: true }}>
<Bubble content="test" />
</XProvider>
</StyleProvider>,
);

expect(container.querySelector('.ant-bubble')).toBeTruthy();
});
});
Loading