Skip to content

fix: skip unchanged native stylesheet reinitialization - #652

Merged
Brentlok merged 1 commit into
uni-stack:mainfrom
juliusmarminge:fix/skip-unchanged-native-styles
Sep 1, 2026
Merged

fix: skip unchanged native stylesheet reinitialization#652
Brentlok merged 1 commit into
uni-stack:mainfrom
juliusmarminge:fix/skip-unchanged-native-styles

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Note

The following is agent reported but I got approval from Hubert to file this so hope you don't mind:
CleanShot 2026-08-31 at 09 54 05@2x


We found this while profiling slow Fast Refresh in T3 Code Mobile with Hermes CPU samples and React timing traces against a populated environment.

A comment-only edit to an unmounted screen caused Uniwind to reinitialize the native stylesheet, clear style caches, and call notifyAll(), even though the generated styles hadn't changed. That made unrelated mounted components do work. Instrumenting __reinit and temporarily bypassing it for that edit confirmed the source of the extra work.

This patch fingerprints the generated native stylesheet and theme list in the Metro transformer. In development, __reinit skips reinitialization when the fingerprint matches the last successful initialization.

CSS compilation and class discovery still run. Hashing the generated output matters because edits to component files can introduce new Tailwind classes without changing the CSS source.

For the comparison, we used the same patched build and toggled only the fingerprint guard. The control omitted the fingerprint argument; the treatment supplied it. Measurements cover the same two-second window around a comment-only edit.

Metric Before After
Home: active sampled JS time 163.2 ms 92.8 ms
Home: React timing entries 636 270
Large thread: active sampled JS time 237.2 ms 96.1 ms
Large thread: React timing entries 879 287

That's approximately 43% less sampled JS work on Home and 59% less on the large thread.

These were individual matched simulator runs using Expo 57.0.18, React Native 0.86.3, and Uniwind 1.7.0 on an iPhone 17 Pro simulator running iOS 26.5. They measure work around the edit, not end-to-end Fast Refresh latency.

We subsequently rebased and verified the patch on Uniwind 1.11.0:

  • Three edits with unchanged generated styles each caused zero stylesheet rebuilds and zero global notifications.
  • Adding and removing a Tailwind candidate each caused one rebuild and one notification.
  • Fast Refresh preserved the JS runtime; native light/dark switching still worked.

Changed styles or theme lists still initialize normally. The fingerprint is stored only after initialization succeeds, allowing failed initialization to retry. Calls without a fingerprint, production initialization, and the web path retain their existing behavior.

Summary by CodeRabbit

  • Performance

    • Improved native development performance by skipping redundant style reinitialization when styles and themes are unchanged.
    • Native style updates still reinitialize correctly when changes are detected.
  • Bug Fixes

    • Improved recovery after a failed style initialization so subsequent attempts can proceed normally.
  • Documentation

    • Updated build and bundler documentation to describe native style fingerprinting and reinitialization behavior.

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e049712e-abcc-4544-ba8f-5c5388a56a30

📥 Commits

Reviewing files that changed from the base of the PR and between deaef56 and 7e2c5a0.

📒 Files selected for processing (4)
  • CONTEXT.md
  • packages/uniwind/src/bundler/adapters/metro/transformer.ts
  • packages/uniwind/src/core/config/config.native.ts
  • packages/uniwind/tests/native/core/config.test.ts

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Native Metro bundles now compute SHA-256 fingerprints from compiled CSS and themes. Native development configuration skips reinitialization for unchanged fingerprints and retries after failures. Tests cover unchanged, changed, and failed initialization cases.

Changes

Native style fingerprinting

Layer / File(s) Summary
Generate and pass native style fingerprints
packages/uniwind/src/bundler/adapters/metro/transformer.ts, CONTEXT.md
Native Metro output hashes compiled CSS and themes, then passes the fingerprint to Uniwind.__reinit. The documentation describes this behavior.
Skip unchanged native reinitialization
packages/uniwind/src/core/config/config.native.ts, packages/uniwind/tests/native/core/config.test.ts
Native configuration stores the latest fingerprint and skips matching development reinitialization. Tests cover changed fingerprints and retries after initialization failures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🔵 Low · up to 7e2c5

The change reduces unnecessary native stylesheet refresh work during development, but a partially failed refresh could leave theme and style-store state inconsistent and cause a later rollback to be skipped. The PR is mergeable with explicit owner awareness or follow-up coverage for this recovery path.

Suggested reviewers: brentlok

Sequence Diagram(s)

sequenceDiagram
  participant MetroTransformer
  participant NativeBundle
  participant UniwindConfig
  participant StyleStore
  MetroTransformer->>MetroTransformer: Hash compiled CSS and themes
  MetroTransformer->>NativeBundle: Emit __reinit with fingerprint
  NativeBundle->>UniwindConfig: Call __reinit
  UniwindConfig->>StyleStore: Initialize styles when fingerprint changed
  UniwindConfig-->>NativeBundle: Skip unchanged development initialization
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: avoiding native stylesheet reinitialization when generated styles are unchanged.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown

Greptile Summary

The PR fingerprints generated native stylesheet data and theme names so development Fast Refresh can avoid rebuilding unchanged style state. Major changes:

  • Metro emits a SHA-256 fingerprint with native Uniwind.__reinit calls.
  • The native configuration skips reinitialization when the last successful fingerprint matches.
  • Tests cover unchanged output, changed output, and retries after initialization failure.
  • Runtime behavior is documented in CONTEXT.md.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness or security issues identified.

The fingerprint covers all state consumed by native stylesheet initialization, is checked only after a successful prior initialization, and preserves retry and unfingerprinted-call behavior.

Important Files Changed

Filename Overview
packages/uniwind/src/bundler/adapters/metro/transformer.ts Hashes the complete generated native initialization inputs and embeds the fingerprint in the generated module.
packages/uniwind/src/core/config/config.native.ts Adds a development-only equality guard and commits the fingerprint only after successful initialization.
packages/uniwind/tests/native/core/config.test.ts Covers unchanged and changed fingerprints as well as retry behavior following an initialization exception.
CONTEXT.md Documents fingerprint-based native stylesheet reinitialization behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Metro compiles native CSS] --> B[Hash generated styles and themes]
    B --> C[Call Uniwind.__reinit]
    C --> D{Development fingerprint matches?}
    D -->|Yes| E[Skip stylesheet rebuild]
    D -->|No| F[Update configured themes]
    F --> G[Reinitialize native style store]
    G --> H[Store fingerprint after success]
Loading

Reviews (1): Last reviewed commit: "fix: skip unchanged native stylesheet re..." | Re-trigger Greptile

@Brentlok Brentlok left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great contribution! 🚀

@Brentlok
Brentlok merged commit 0a05375 into uni-stack:main Sep 1, 2026
2 checks passed
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🚀 This pull request is included in v1.12.0. See Release v1.12.0 for release notes.

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.

2 participants