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
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
15 changes: 12 additions & 3 deletions packages/x/components/theme/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,19 @@ export const getComputedToken = (
};
export function useInternalToken(): [
theme: Theme<SeedToken, AliasToken>,
token: GlobalToken,
hashId: string,
realToken: GlobalToken,
hashId: string,
token: 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