Skip to content

WIP: zero-runtime static extraction prototype#213

Draft
mise42 wants to merge 13 commits into
ant-design:masterfrom
mise42:feat/zero-runtime-prototype
Draft

WIP: zero-runtime static extraction prototype#213
mise42 wants to merge 13 commits into
ant-design:masterfrom
mise42:feat/zero-runtime-prototype

Conversation

@mise42
Copy link
Copy Markdown

@mise42 mise42 commented Mar 10, 2026

Summary

  • add a zero-runtime extraction prototype around createStaticStyles
  • wire stable styleId injection, compiled chunk collection, manifest hydration, and webpack/vite emit plugins
  • extend static collection to cover object and atom returns, cssVar, responsive, cx, keyframes, local constants, and optional imported-value resolution
  • add/update verification coverage and roadmap notes for the current extraction boundary

Validation

  • npm run verify:extract-prototype
  • npm run type-check

Extended ON/OFF comparison (zero-runtime extraction)

Below is the latest comparison from the integrated LobeHub SPA validation path using:

  • ON: ANTD_STYLE_EXTRACT=1
  • OFF: ANTD_STYLE_EXTRACT=0

1) Functional correctness (ON)

Strict extraction checks pass end-to-end:

  • manifest exists and is non-empty
  • extracted css exists and is non-empty
  • HTML contains the extracted css link
  • no %BASE_URL% placeholder leakage in built HTML
  • hydration path is mounted (hydrateExtractedStyles)
  • manifest hit returns classMap
  • manifest miss keeps runtime fallback behavior (no render break)

Result: pass=18 / warn=0 / fail=0

2) Bundle output (ON vs OFF)

Metric ON OFF Delta (ON-OFF)
Total JS (raw) 49,760,112 B 49,749,294 B +10,818 B
Total JS (gzip) 12,344,785 B 12,339,508 B +5,277 B
Initial JS (raw) ~same ~same +19 B
Initial JS (gzip) ~same ~same +3 B

Additional ON-only artifacts:

  • __antd-style.extract.css: 234,108 B (gzip 28,560 B)
  • __antd-style.extract.manifest.json: 91,453 B (gzip 15,677 B)

3) Runtime user-story perf (ON vs OFF)

Logged-in route-transition comparison (same user-story flow, multi-round):

  • steady-state delta (ON-OFF): **~ -1.0 ms**
  • interpretation: no regression observed

Notes

  • this PR is intentionally marked WIP/draft
  • the branch is a prototype spike for upstream discussion, not a finished production rollout
  • caveat on perf methodology: dev-mode measurements are noisy for this feature; production-build comparisons are more representative. Lighthouse remains useful for CWV snapshots, but extraction’s cacheability benefit is better reflected in repeated/warm-cache real flows.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 10, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9f6ec041-49c4-42bd-ada9-6a84684911c9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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
Copy Markdown

Summary of Changes

Hello, 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 a foundational prototype for a zero-runtime static CSS extraction architecture within antd-style. The primary goal is to shift style processing from runtime to compile-time, thereby enhancing application performance and reducing JavaScript overhead. It lays the groundwork for future static analysis and optimization by integrating with build tools like Webpack and Vite, and providing mechanisms for stable style identification and hydration.

Highlights

  • Zero-Runtime Extraction Prototype: Introduced a prototype for zero-runtime static CSS extraction around createStaticStyles, aiming to improve performance by moving style generation to compile-time.
  • Style ID Injection and Manifest Hydration: Implemented stable styleId injection, a mechanism for collecting compiled chunks, and an API for hydrating the extracted style manifest at runtime.
  • Bundler Integration: Added experimental Webpack and Vite emit plugins to handle the output of __antd-style.extract.manifest.json and __antd-style.extract.css assets.
  • Enhanced Static Collection: Extended the static collection capabilities to cover object and atom returns, cssVar interpolation, responsive breakpoints, cx composition, keyframes, local constants, and optional imported-value resolution.
  • Runtime Mode Switch: Added a runtime mode switch (runtime | extract) and associated APIs (setStyleRuntimeMode, registerExtractedStyles, getExtractedStyles, clearExtractedStyles) to manage how styles are consumed.
  • Babel Plugin for Static Analysis: Developed an experimental Babel plugin (babelInjectStyleId) to annotate createStaticStyles calls with stable styleIds and perform best-effort static collection.
  • Collector Bridges: Introduced both compiled and experimental runtime collector bridges to gather style chunks during the extraction process.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • BRANCH_CHANGELOG.md
    • Added a detailed changelog for the feat/zero-runtime-prototype branch, outlining the summary, included commits, and key changes of the prototype.
  • ZERO_RUNTIME_TASKS.md
    • Added a comprehensive task list in Chinese for the antd-style zero-runtime extraction project, detailing P0, P1, P2, and P3 milestones, as well as risks and suggested next steps.
  • package.json
    • Added a new verify:extract-prototype script to run verification tests for the extraction prototype.
  • src/core/index.ts
    • Exported all members from the new styleEngine module.
  • src/core/styleEngine.ts
    • Added a new file defining StyleRuntimeMode (runtime | extract), ExtractedStylePayload interface, and functions (getStyleRuntimeMode, setStyleRuntimeMode, registerExtractedStyles, getExtractedStyles, clearExtractedStyles) to manage the style engine's runtime behavior and extracted style registry.
  • src/extract/ROADMAP.md
    • Added a roadmap document outlining the implemented features and next steps for the extract pipeline MVP.
  • src/extract/babelInjectStyleId.ts
    • Added a new Babel plugin (babelInjectStyleId) that injects stable styleIds into createStaticStyles calls and performs best-effort static collection of styles, including support for cssVar, responsive, cx, keyframes, and local constants.
  • src/extract/collector.ts
    • Added a new file defining a runtime collector (pushExtractedChunk, pullExtractedChunks, clearExtractedChunks) for ExtractedChunks, used as a legacy fallback.
  • src/extract/compiledCollector.ts
    • Added a new file defining a compiled collector (pushCompiledExtractChunk, pullCompiledExtractChunks, clearCompiledExtractChunks) for ExtractStyleChunks, used by bundler transforms.
  • src/extract/core.ts
    • Added a new file with buildExtractAssets function to assemble extracted style chunks into a manifest JSON and a combined CSS string, with options for deduplication and sorting.
  • src/extract/hydrateExtractedStyles.ts
    • Added a new function hydrateExtractedStyles to load a compile-time extracted style manifest into the antd-style runtime and switch the style engine to 'extract' mode.
  • src/extract/index.ts
    • Exported all new modules related to the extraction pipeline, including Babel plugins, collectors, core utilities, hydration, style ID generation, and bundler plugins.
  • src/extract/styleId.ts
    • Added a new utility function createStyleId to generate stable, hashed style identifiers based on input and an optional salt.
  • src/extract/types.ts
    • Added new type definitions for ExtractStyleEntry, ExtractStyleChunk, ExtractStyleManifest, ExtractAssetBuildOptions, ExtractAssetBuildResult, StaticCollectRule, StaticCollectImportMeta, StaticCollectOptions, and plugin options for Webpack and Vite.
  • src/extract/viteEmitPlugin.ts
    • Added a new Vite/Rollup plugin (AntdStyleExtractVitePlugin) for zero-runtime extraction, which emits manifest and CSS assets and includes an optional static collection mode in its transform hook.
  • src/extract/webpackEmitPlugin.ts
    • Added a new Webpack plugin (AntdStyleExtractWebpackPlugin) for zero-runtime extraction, responsible for emitting manifest JSON and CSS files from extracted chunks, with support for experimental static collection from compilation modules.
  • src/factories/createStaticStyles/index.ts
    • Modified createStaticStyles to accept an optional options object including styleId, enabling it to consume pre-extracted class maps in 'extract' runtime mode or fall back to runtime generation. It also includes an experimental runtime collector bridge for ANTD_STYLE_EXTRACT_COLLECT=1.
  • src/factories/createStaticStyles/types.ts
    • Added StaticStylesOptions interface to define internal options for createStaticStyles, specifically for styleId injection.
  • src/factories/createStyleProvider/index.tsx
    • Updated references from global to globalThis for accessing the SSR cache manager, improving compatibility across different JavaScript environments.
  • src/functions/createInstance.ts
    • Updated references from global to globalThis for accessing the SSR cache manager, improving compatibility across different JavaScript environments.
  • src/functions/extractStaticStyle.tsx
    • Updated references from global to globalThis for accessing the SSR cache manager, improving compatibility across different JavaScript environments and ensuring proper cache retrieval.
  • src/functions/index.ts
    • Exported new experimental zero-runtime extract controls, including clearExtractedStyles, getStyleRuntimeMode, registerExtractedStyles, setStyleRuntimeMode, and their corresponding types.
  • src/index.ts
    • Exported all new modules and types from the extract directory, making them available in the main library entry point.
  • tests/snapshots/export.test.tsx.snap
    • Updated the export snapshot to include all newly added extraction-related functions and types.
  • tests/hooks/useResponsive.test.ts
    • Added xxxl to the responsive test snapshot, reflecting an update in responsive breakpoint handling.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/test.yml
Activity
  • The pull request is marked as 'WIP/draft', indicating it's an ongoing development and not yet ready for final review or production deployment.
  • The author, mise42, explicitly states that this branch is a 'prototype spike for upstream discussion', highlighting its experimental nature and intent to gather feedback.
  • Verification steps are provided, including npm run verify:extract-prototype and npm run type-check, to ensure the prototype's functionality and type safety.
  • The ZERO_RUNTIME_TASKS.md file outlines a detailed plan and milestones for the project, suggesting a structured approach to its development.
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
Copy Markdown

@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 comprehensive prototype for zero-runtime static style extraction for antd-style, adding new infrastructure like Babel, Webpack, and Vite plugins for compile-time style collection. The core logic involves statically analyzing createStaticStyles calls, generating stable IDs, and extracting CSS and class name maps into a manifest, with the runtime updated to hydrate and use these pre-compiled styles. A critical prototype pollution vulnerability was identified in the Babel plugin's static evaluator, which could be exploited by malicious source code to affect the build environment. Furthermore, a high-risk dependency on an internal property of the Emotion cache was noted, which is brittle and could break with dependency updates. Specific comments provide suggestions for improving robustness and addressing the identified issues.

: undefined;
if (!hash) return undefined;

const cssText = collector.cache.inserted?.[hash];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Accessing collector.cache.inserted is risky as it relies on an internal, undocumented property of Emotion's cache. This can break with minor updates to Emotion. While this is a prototype, it would be more robust to find a way to retrieve the CSS text using public APIs if possible. This same pattern is also used in collectKeyframesTaggedTemplate.

? cls.slice(emotionCache.key.length + 1)
: undefined;
if (!hash) return;
const inserted = (emotionCache.inserted as any)?.[hash];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Accessing emotionCache.inserted relies on an internal implementation detail of Emotion's cache, which is not guaranteed to be stable across versions. This could lead to breakages when Emotion is updated. For a more robust implementation, consider if there's a way to achieve this using Emotion's public APIs.

Comment on lines +487 to +499
for (const property of node.properties) {
if (!t.isObjectProperty(property)) return undefined;

const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key) return undefined;

const value = resolveStaticExpression(t, property.value, context, callPath, visiting);
if (value === undefined) return undefined;

result[key] = value;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The resolveStaticExpression function is vulnerable to prototype pollution. When evaluating object expressions from the source code, it uses property keys directly to set values on a fresh object {}. If a malicious source file contains an object with a __proto__ key, it will pollute the Object.prototype of the Babel process. This could potentially lead to Remote Code Execution (RCE) if other parts of the build pipeline are vulnerable to prototype pollution. To fix this, validate that the property key is not __proto__, constructor, or prototype before setting it on the object, or use Object.create(null) for the result object.

Suggested change
for (const property of node.properties) {
if (!t.isObjectProperty(property)) return undefined;
const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key) return undefined;
const value = resolveStaticExpression(t, property.value, context, callPath, visiting);
if (value === undefined) return undefined;
result[key] = value;
for (const property of node.properties) {
if (!t.isObjectProperty(property)) return undefined;
const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key || key === '__proto__' || key === 'constructor' || key === 'prototype') return undefined;
const value = resolveStaticExpression(t, property.value, context, callPath, visiting);
if (value === undefined) return undefined;
result[key] = value;

Comment on lines +952 to +966
for (const property of returned.properties) {
if (!t.isObjectProperty(property)) return undefined;

const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key) return undefined;

const collected = collectStyleValue(t, property.value, context, callPath, collector);
if (!collected || !collected.className) return undefined;

styles[key] = collected.className;
pushCssTexts(collected.cssTexts);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The collectStaticChunk function is vulnerable to prototype pollution. When collecting styles from the source code, it uses property keys directly to set values on a fresh object {}. If a malicious source file contains an object with a __proto__ key, it will pollute the Object.prototype of the Babel process. To fix this, validate that the property key is not __proto__, constructor, or prototype before setting it on the object.

Suggested change
for (const property of returned.properties) {
if (!t.isObjectProperty(property)) return undefined;
const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key) return undefined;
const collected = collectStyleValue(t, property.value, context, callPath, collector);
if (!collected || !collected.className) return undefined;
styles[key] = collected.className;
pushCssTexts(collected.cssTexts);
}
for (const property of returned.properties) {
if (!t.isObjectProperty(property)) return undefined;
const key =
t.isIdentifier(property.key) || t.isStringLiteral(property.key)
? property.key.name || property.key.value
: undefined;
if (!key || key === '__proto__' || key === 'constructor' || key === 'prototype') return undefined;
const collected = collectStyleValue(t, property.value, context, callPath, collector);
if (!collected || !collected.className) return undefined;
styles[key] = collected.className;
pushCssTexts(collected.cssTexts);
}

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.

1 participant