Skip to content

refactor: major bundler refactor#523

Merged
Brentlok merged 17 commits into
mainfrom
major-refactor
May 18, 2026
Merged

refactor: major bundler refactor#523
Brentlok merged 17 commits into
mainfrom
major-refactor

Conversation

@Brentlok

@Brentlok Brentlok commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Bundler pipeline now emits platform-aware CSS artifacts and generated type declarations for Metro and Vite.
    • useCSSVariable hook extended to accept arrays and includes a new helper for resolving CSS variables.
  • Refactor

    • Centralized bundler configuration and unified adapter flow for consistent theme handling and artifact generation.
  • Chores

    • Added project documentation and updated Jest module mapping for local imports.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Centralize bundler config and pipeline (UniwindBundlerConfig), add Tailwind→platform CSS compilers, move Metro/Vite adapters under src/bundler/adapters, consolidate consts/types to src/common, add build/tsconfig/Jest aliases, and migrate many imports to type-only.

Changes

Bundler Architecture & Configuration Refactor

Layer / File(s) Summary
Bundler config & factories
src/bundler/config.ts
Add UniwindBundlerConfig with fromMetroConfig/fromViteConfig, computed getters, toMetroConfig() and generateArtifacts() for CSS/DTS generation.
CSS compilation pipeline & artifacts
src/bundler/css-compiler/*, src/bundler/artifacts/css/index.ts, src/bundler/artifacts/dts.ts
Add compileTailwind, compileCSS, compileWebCSS, compileNativeCSS; change buildCSS to accept cssFilePath; make DTS module identity static (declare module 'uniwind').
Metro adapter & transformer
src/bundler/adapters/metro/*
New Metro adapter using UniwindBundlerConfig; new transformer that loads metro worker, intercepts injected web module and CSS entry file, compiles/injects platform code, writes uniwind.css, delegates to worker, and adjusts Metro outputs.
Vite adapter refactor
src/bundler/adapters/vite/vite.ts
Vite plugin now constructs bundlerConfig via fromViteConfig, uses bundlerConfig.cssVisitor/stringifiedThemes, and calls bundlerConfig.generateArtifacts during build.
Build config & aliases
build.config.ts, tsconfig.json, jest.config.*
Add srcPath helper, rollup/mkdist entries for adapter bundles (including Metro transformer), emit dist/metro & dist/vite, add @/bundler alias, set rootDir: src and paths for @/common/* and @/bundler/*, and add Jest moduleNameMapper for ^@/(.*)$.
CSS processor / visitor reorg
src/bundler/css-processor/*, src/bundler/css-visitor/*
Rehome shared consts to @/common/consts, convert many lightningcss imports to import type, add css-processor barrel exports, and update serialize logger import to @/bundler/logger.
Types & consts relocation
src/common/consts.ts, src/bundler/types.ts, src/core/types.ts
Introduce StyleDependency, Orientation, ColorScheme in common/consts; add bundler types (UniwindConfig, Polyfills, UniwindMetroConfig); add empty UniwindConfig interface in core/types and consolidate type exports.
Component/hook import hygiene
src/components/*, src/hooks/*, src/hoc/*, src/core/*
Convert many prop/type imports to import type, and update imports to use @/common/consts for StyleDependency/ColorScheme/Orientation.
Tests / setup updates
tests/*
Update E2E and native test setup to use UniwindBundlerConfig + compileCSS pipeline instead of deprecated compileVirtual.
Legacy cleanup
src/metro/*
Stop re-exporting legacy src/metro/* symbols and remove/replace old metro-transformer/compileVirtual/injectThemes usages.

Sequence Diagram(s)

sequenceDiagram
  participant VitePlugin
  participant UniwindBundlerConfig
  participant compileCSS
  participant MetroTransformer
  participant MetroWorker
  VitePlugin->>UniwindBundlerConfig: fromViteConfig(config)
  VitePlugin->>UniwindBundlerConfig: generateArtifacts(cssArtifactPath)
  UniwindBundlerConfig->>compileCSS: cssPath
  compileCSS->>UniwindBundlerConfig: compiled CSS / serialized artifacts
  MetroTransformer->>UniwindBundlerConfig: fromMetroConfig(config)
  MetroTransformer->>compileCSS: compile CSS for cssEntryFile
  MetroTransformer->>MetroWorker: delegate transform(rewritten data)
  MetroWorker-->>MetroTransformer: transformed output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • uni-stack/uniwind#521: Related — useCSSVariable/getCSSVariable changes overlap with this PR’s hook updates.
  • uni-stack/uniwind#483: Related — Metro transformer CSS detection/transform logic overlaps with the new adapter transformer.
  • uni-stack/uniwind#460: Related — public type export changes around ThemeName/UniwindConfig.

Suggested reviewers

  • jpudysz

Poem

"🐰 I hopped through code and thread,
Bundler maps and types ahead,
Themes compiled, adapters moved,
Imports cleaned and paths improved —
A little hop for devs well-fed."

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch major-refactor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts (1)

77-85: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resync immediately when the Uniwind context changes.

Lines 77-85 resubscribe on uniwindContext changes but never recompute the current value. After a provider swap, this hook keeps the old context's variable values until Theme or Variables emits again.

Suggested fix
     useLayoutEffect(() => {
         const updateValue = () => setValue(getCSSVariable(nameRef.current, uniwindContext))
+        updateValue()
         const dispose = UniwindListener.subscribe(
             updateValue,
             [StyleDependency.Theme, StyleDependency.Variables],
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts` around lines 77
- 85, The hook currently resubscribes on uniwindContext change but doesn't
recompute the variable value immediately; inside the useLayoutEffect in
useCSSVariable, call updateValue() (which uses getCSSVariable(nameRef.current,
uniwindContext) and setValue) right before creating the subscription (or
immediately after) so the value reflects the new uniwindContext immediately,
then create the UniwindListener.subscribe(...) and return dispose as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/uniwind/src/bundler/adapters/metro/transformer.ts`:
- Around line 33-35: The path comparisons in the isCss check and the
metro-injected.js check are brittle across platforms; normalize and resolve both
sides before comparing. Concretely, compute normalized absolute paths (e.g.,
cssEntryPath = path.normalize(path.resolve(process.cwd(),
config.uniwind.cssEntryFile)) and normalizedFilePath =
path.normalize(path.resolve(projectRoot, filePath))) and use strict equality
(cssEntryPath === normalizedFilePath) instead of joining then comparing; for the
injected file replace the endsWith('/components/web/metro-injected.js') test
with the same normalized-resolved comparison against
path.normalize(path.resolve(projectRoot, 'components', 'web',
'metro-injected.js')) so matching works on Windows and in monorepos while
keeping the options.type !== 'asset' check.

In `@packages/uniwind/src/bundler/adapters/vite/vite.ts`:
- Around line 81-84: The injected Uniwind.__reinit call is concatenated directly
to the transformed module text (variable code), which can break when the
original ends with comments or relies on ASI; update the return in the
normalizedId branch so you add an explicit statement boundary before calling
Uniwind.__reinit (e.g., prepend a semicolon or newline+semicolon to the injected
snippet) when building the code string that uses
bundlerConfig.stringifiedThemes, ensuring the injected call is always a separate
statement.

In `@packages/uniwind/src/bundler/config.ts`:
- Around line 67-69: The stringifiedThemes getter emits raw single-quoted theme
names which can break when a theme contains quotes or backslashes; replace its
manual quoting with a proper JSON serialization by returning
JSON.stringify(this.themes) from the stringifiedThemes getter (i.e., update the
stringifiedThemes getter that references this.themes to use JSON.stringify to
produce a safe JS array literal).
- Around line 45-47: The cssPath getter currently uses process.cwd() which
breaks monorepos; update UniwindBundlerConfig to accept and store an explicit
project root (e.g., projectRoot or root) via its constructor or factory, replace
process.cwd() in the cssPath getter with that stored property so cssPath returns
path.join(this.projectRoot, this.config.cssEntryFile), and ensure callers that
construct UniwindBundlerConfig are updated to pass the correct app root.

In `@packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts`:
- Around line 24-26: The code that builds serializedScopedVars interpolates
scopedVarsName directly into a JS string literal, which breaks/permits injection
if the key contains " or \; update the serialization in compileNativeCSS.ts so
scopedVarsName is escaped before embedding (e.g., use
JSON.stringify(scopedVarsName) or an escape helper) when constructing
serializedScopedVars, keeping the same output shape (`"name": ({ ... }),`) and
preserving the trailing comma; ensure you apply this change where
serializedScopedVars is computed so both quotes and backslashes are properly
escaped.

In `@packages/uniwind/src/bundler/css-compiler/compileTailwind.ts`:
- Line 8: The compileTailwind code currently uses blocking I/O via
fs.readFileSync when loading bundlerConfig.cssPath; change this to non-blocking
fs.promises.readFile and await the result (e.g., await
fs.promises.readFile(bundlerConfig.cssPath, 'utf-8')) so the function does not
block the event loop—ensure the surrounding function (e.g., compileTailwind) is
async or already awaits this call and replace the sync variable assignment
(const css = fs.readFileSync(...)) with the awaited promise-based read.

In `@packages/uniwind/tsconfig.json`:
- Around line 13-19: tsconfig.json contains broken path aliases "@/metro" and
"@/utils/*" that point to non-existent targets; update the "paths" section so
"@/metro" either points to the actual metro adapter entry (replace the current
target) or remove the "@/metro" alias and import via "@/bundler/adapters/metro",
and either remove or correct the "@/utils/*" alias to point to a real utils
directory (or create a re-export shim if you prefer keeping the alias); ensure
the "@/bundler/*" mapping remains correct and run type-checking to confirm the
fixes.

---

Outside diff comments:
In `@packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts`:
- Around line 77-85: The hook currently resubscribes on uniwindContext change
but doesn't recompute the variable value immediately; inside the useLayoutEffect
in useCSSVariable, call updateValue() (which uses
getCSSVariable(nameRef.current, uniwindContext) and setValue) right before
creating the subscription (or immediately after) so the value reflects the new
uniwindContext immediately, then create the UniwindListener.subscribe(...) and
return dispose as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1fba412-f33d-4235-8134-8f7501a66b47

📥 Commits

Reviewing files that changed from the base of the PR and between a2406b8 and d7e458f.

📒 Files selected for processing (69)
  • packages/uniwind/build.config.ts
  • packages/uniwind/jest.config.native.js
  • packages/uniwind/jest.config.web.js
  • packages/uniwind/src/bundler/adapters/metro/index.d.ts
  • packages/uniwind/src/bundler/adapters/metro/index.ts
  • packages/uniwind/src/bundler/adapters/metro/metro.ts
  • packages/uniwind/src/bundler/adapters/metro/patches.ts
  • packages/uniwind/src/bundler/adapters/metro/resolvers.ts
  • packages/uniwind/src/bundler/adapters/metro/transformer.ts
  • packages/uniwind/src/bundler/adapters/vite/index.d.ts
  • packages/uniwind/src/bundler/adapters/vite/index.ts
  • packages/uniwind/src/bundler/adapters/vite/vite.ts
  • packages/uniwind/src/bundler/artifacts/css/extraUtilities.ts
  • packages/uniwind/src/bundler/artifacts/css/index.ts
  • packages/uniwind/src/bundler/artifacts/css/insets.ts
  • packages/uniwind/src/bundler/artifacts/css/overwrite.ts
  • packages/uniwind/src/bundler/artifacts/css/themes.ts
  • packages/uniwind/src/bundler/artifacts/css/variants.ts
  • packages/uniwind/src/bundler/artifacts/dts.ts
  • packages/uniwind/src/bundler/config.ts
  • packages/uniwind/src/bundler/css-compiler/compileCSS.ts
  • packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts
  • packages/uniwind/src/bundler/css-compiler/compileTailwind.ts
  • packages/uniwind/src/bundler/css-compiler/compileWebCSS.ts
  • packages/uniwind/src/bundler/css-compiler/index.ts
  • packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts
  • packages/uniwind/src/bundler/css-processor/color.ts
  • packages/uniwind/src/bundler/css-processor/css.ts
  • packages/uniwind/src/bundler/css-processor/functions.ts
  • packages/uniwind/src/bundler/css-processor/index.ts
  • packages/uniwind/src/bundler/css-processor/mq.ts
  • packages/uniwind/src/bundler/css-processor/processor.ts
  • packages/uniwind/src/bundler/css-processor/rn.ts
  • packages/uniwind/src/bundler/css-processor/serialize.ts
  • packages/uniwind/src/bundler/css-processor/types.ts
  • packages/uniwind/src/bundler/css-processor/units.ts
  • packages/uniwind/src/bundler/css-processor/utils.ts
  • packages/uniwind/src/bundler/css-processor/var.ts
  • packages/uniwind/src/bundler/css-visitor/function-visitor.ts
  • packages/uniwind/src/bundler/css-visitor/index.ts
  • packages/uniwind/src/bundler/css-visitor/rule-visitor.ts
  • packages/uniwind/src/bundler/css-visitor/visitor.ts
  • packages/uniwind/src/bundler/logger.ts
  • packages/uniwind/src/bundler/types.ts
  • packages/uniwind/src/common/consts.ts
  • packages/uniwind/src/components/web/rnw.ts
  • packages/uniwind/src/core/config/config.common.ts
  • packages/uniwind/src/core/config/config.native.ts
  • packages/uniwind/src/core/config/config.ts
  • packages/uniwind/src/core/listener.ts
  • packages/uniwind/src/core/native/runtime.ts
  • packages/uniwind/src/core/native/store.ts
  • packages/uniwind/src/core/types.ts
  • packages/uniwind/src/core/web/cssListener.ts
  • packages/uniwind/src/hoc/withUniwind.native.tsx
  • packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts
  • packages/uniwind/src/hooks/useUniwind.ts
  • packages/uniwind/src/index.ts
  • packages/uniwind/src/metro/compileVirtual.ts
  • packages/uniwind/src/metro/index.ts
  • packages/uniwind/src/metro/injectThemes.ts
  • packages/uniwind/src/metro/metro-transformer.ts
  • packages/uniwind/src/metro/processor/index.ts
  • packages/uniwind/src/metro/utils/index.ts
  • packages/uniwind/src/types.ts
  • packages/uniwind/src/utils/stringifyThemes.ts
  • packages/uniwind/tests/e2e/global-setup.ts
  • packages/uniwind/tests/setup.native.ts
  • packages/uniwind/tsconfig.json
💤 Files with no reviewable changes (8)
  • packages/uniwind/src/metro/utils/index.ts
  • packages/uniwind/src/utils/stringifyThemes.ts
  • packages/uniwind/src/metro/injectThemes.ts
  • packages/uniwind/src/metro/processor/index.ts
  • packages/uniwind/src/metro/index.ts
  • packages/uniwind/src/types.ts
  • packages/uniwind/src/metro/compileVirtual.ts
  • packages/uniwind/src/metro/metro-transformer.ts

Comment thread packages/uniwind/src/bundler/adapters/metro/transformer.ts
Comment thread packages/uniwind/src/bundler/adapters/vite/vite.ts
Comment thread packages/uniwind/src/bundler/config.ts
Comment thread packages/uniwind/src/bundler/config.ts
Comment thread packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts
Comment thread packages/uniwind/src/bundler/css-compiler/compileTailwind.ts Outdated
Comment thread packages/uniwind/tsconfig.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
packages/uniwind/src/bundler/adapters/metro/transformer.ts (1)

35-37: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize/resolve paths before matching special files.

Current comparisons are brittle across Windows and some monorepo layouts, which can skip both CSS compilation and metro-injected.js handling.

Proposed fix
-    const isCss = options.type !== 'asset' && path.join(process.cwd(), config.uniwind.cssEntryFile) === path.join(projectRoot, filePath)
+    const normalizedFilePath = path.normalize(path.resolve(projectRoot, filePath))
+    const cssEntryPath = path.normalize(path.resolve(process.cwd(), config.uniwind.cssEntryFile))
+    const injectedPath = path.normalize(path.resolve(projectRoot, 'components', 'web', 'metro-injected.js'))
+    const isCss = options.type !== 'asset' && normalizedFilePath === cssEntryPath
 
-    if (filePath.endsWith('/components/web/metro-injected.js')) {
+    if (normalizedFilePath === injectedPath) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/uniwind/src/bundler/adapters/metro/transformer.ts` around lines 35 -
37, The path comparisons are brittle; normalize and resolve both sides before
matching: for the CSS check (isCss) resolve/normalize
config.uniwind.cssEntryFile against process.cwd() and resolve/normalize
projectRoot + filePath (or resolve filePath) before comparing, and for the
special file check resolve/normalize the target string (e.g.,
path.join('components','web','metro-injected.js')) and compare it against a
resolved/normalized filePath instead of using filePath.endsWith(...); update the
logic around the isCss computation and the if that references
'/components/web/metro-injected.js' to use these normalized/resolved paths so
comparisons work cross-platform and in monorepos.
packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts (1)

24-26: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Escape scopedVarsName before embedding generated JS.

scopedVarsName is interpolated raw into a quoted key, so keys containing " or \ can produce invalid/generated-injectable code.

Proposed fix
 const serializedScopedVars = Object.entries(scopedVars)
-    .map(([scopedVarsName, scopedVars]) => `"${scopedVarsName}": ({ ${scopedVars} }),`)
+    .map(([scopedVarsName, scopedVars]) => `${JSON.stringify(scopedVarsName)}: ({ ${scopedVars} }),`)
     .join('')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts` around lines
24 - 26, The generated JS embeds scopedVarsName raw into a quoted key which can
break if the name contains `"` or `\`; update the serializedScopedVars
construction in compileNativeCSS.ts (the const serializedScopedVars) to escape
scopedVarsName before embedding (e.g., use JSON.stringify(scopedVarsName) or
explicitly replace `\` and `"` with `\\`/`\"`) so the produced key is a valid JS
string literal and cannot inject invalid code.
🧹 Nitpick comments (3)
context.md (2)

157-158: ⚡ Quick win

Consider varying sentence structure.

Two consecutive sentences begin with "Web wrappers", which slightly impacts readability.

✨ Suggested improvement
 - Web wrappers import from `react-native` as resolved by bundler aliases.
-- Web wrappers map `className` to RNW CSS style markers through `toRNWClassName`.
-- Web wrappers pass generated `dataSet` so data attribute variants can match.
+- The `className` prop is mapped to RNW CSS style markers through `toRNWClassName`.
+- Generated `dataSet` is passed so data attribute variants can match.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@context.md` around lines 157 - 158, Two consecutive sentences both starting
with "Web wrappers" reduce readability; rephrase one to vary sentence structure
and improve flow. For example, transform the first sentence that mentions
mapping into an active clause referencing the function name toRNWClassName
(e.g., "toRNWClassName maps className to RNW CSS style markers") and rework the
second to lead with the outcome or purpose mentioning dataSet (e.g., "They also
pass the generated dataSet so data attribute variants can match"), or combine
them into a single sentence that includes both toRNWClassName and dataSet for
clarity.

120-121: ⚡ Quick win

Consider varying sentence structure.

Three consecutive sentences begin with "Vite", which slightly impacts readability. Consider varying the structure for better flow.

✨ Suggested improvement
 - Vite aliases `react-native` to Uniwind web components, except imports from Uniwind internals resolve back to `react-native-web`.
-- Vite replaces RNW `createOrderedCSSStyleSheet` with Uniwind's ordered stylesheet implementation.
-- Vite uses Lightning CSS with `UniwindCSSVisitor`.
-- Vite generates artifacts on `buildStart` and `generateBundle`.
+- The RNW `createOrderedCSSStyleSheet` is replaced with Uniwind's ordered stylesheet implementation.
+- Lightning CSS is used with `UniwindCSSVisitor`.
+- Artifacts are generated on `buildStart` and `generateBundle`.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@context.md` around lines 120 - 121, The three consecutive sentences that
begin with "Vite" (the lines stating "Vite uses Lightning CSS with
`UniwindCSSVisitor`." and "Vite generates artifacts on `buildStart` and
`generateBundle`.") make the paragraph repetitive; rephrase one or two of them
to vary sentence openings and improve flow (for example, start one sentence with
the technology or a gerund/passive phrase like "Using Lightning CSS, Vite..." or
"Artifacts are generated during `buildStart` and `generateBundle`"), or combine
related ideas into a single sentence to avoid repeating the subject.
packages/uniwind/src/bundler/adapters/vite/vite.ts (1)

87-91: ⚡ Quick win

Avoid regenerating the same artifacts in both build hooks.

Vite calls buildStart during both serve and build, while output-generation hooks like generateBundle only run during build. With the current wiring, vite build will execute bundlerConfig.generateArtifacts(cssArtifactPath) twice for the same output. Prefer running it in buildStart only for serve and in generateBundle only for build, or guard it with a once-per-run flag. (vite.dev)

♻️ Possible adjustment
 export const uniwind = (config: UniwindConfig): Plugin => {
     const bundlerConfig = UniwindBundlerConfig.fromViteConfig(config)
+    let command: 'serve' | 'build'

     return {
         name: 'uniwind',
         enforce: 'pre',
+        config: (_, env) => {
+            command = env.command
+            return {
+                css: {
+                    transformer: 'lightningcss',
+                    lightningcss: {
+                        visitor: bundlerConfig.cssVisitor,
+                    },
+                },
+                optimizeDeps: {
+                    exclude: ['uniwind', 'react-native'],
+                    esbuildOptions: {
+                        plugins: [{
+                            name: 'uniwind-esbuild-plugin',
+                            setup: build => {
+                                build.onResolve(
+                                    { filter: /^\.\/createOrderedCSSStyleSheet$/ },
+                                    args => {
+                                        if (normalizePath(args.importer).includes('react-native-web/dist/exports/StyleSheet')) {
+                                            return { path: styleSheetPath }
+                                        }
+                                    },
+                                )
+                            },
+                        }],
+                    },
+                },
+                resolve: {
+                    alias: [
+                        {
+                            find: /^react-native$/,
+                            replacement: componentPath,
+                            customResolver: {
+                                resolveId(_, importer) {
+                                    if (importer !== undefined && normalizePath(importer).includes('uniwind/dist')) {
+                                        return this.resolve('react-native-web')
+                                    }
+
+                                    return componentPath
+                                },
+                            },
+                        },
+                    ],
+                },
+            }
+        },
-        config: () => ({
-            css: {
-                transformer: 'lightningcss',
-                lightningcss: {
-                    visitor: bundlerConfig.cssVisitor,
-                },
-            },
-            optimizeDeps: {
-                exclude: ['uniwind', 'react-native'],
-                esbuildOptions: {
-                    plugins: [{
-                        name: 'uniwind-esbuild-plugin',
-                        setup: build => {
-                            build.onResolve(
-                                { filter: /^\.\/createOrderedCSSStyleSheet$/ },
-                                args => {
-                                    if (normalizePath(args.importer).includes('react-native-web/dist/exports/StyleSheet')) {
-                                        return { path: styleSheetPath }
-                                    }
-                                },
-                            )
-                        },
-                    }],
-                },
-            },
-            resolve: {
-                alias: [
-                    {
-                        find: /^react-native$/,
-                        replacement: componentPath,
-                        customResolver: {
-                            resolveId(_, importer) {
-                                if (importer !== undefined && normalizePath(importer).includes('uniwind/dist')) {
-                                    return this.resolve('react-native-web')
-                                }
-
-                                return componentPath
-                            },
-                        },
-                    },
-                ],
-            },
-        }),
         transform: (code, id) => {
             const normalizedId = normalizePath(id)

             if (normalizedId.includes('uniwind/dist') && normalizedId.includes('config/config.js')) {
                 return {
                     code: `${code}\n;Uniwind.__reinit(() => ({}), ${bundlerConfig.stringifiedThemes})`,
                 }
             }
         },
         buildStart: async () => {
-            await bundlerConfig.generateArtifacts(cssArtifactPath)
+            if (command === 'serve') {
+                await bundlerConfig.generateArtifacts(cssArtifactPath)
+            }
         },
         generateBundle: async () => {
-            await bundlerConfig.generateArtifacts(cssArtifactPath)
+            if (command === 'build') {
+                await bundlerConfig.generateArtifacts(cssArtifactPath)
+            }
         },
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/uniwind/src/bundler/adapters/vite/vite.ts` around lines 87 - 91, The
plugin currently calls bundlerConfig.generateArtifacts(cssArtifactPath) in both
buildStart and generateBundle causing duplicate work; modify the Vite hooks so
artifacts are generated only once per run by either 1) adding a module-scoped
boolean flag (e.g., artifactsGenerated) that buildStart and generateBundle
check/set before calling bundlerConfig.generateArtifacts(cssArtifactPath), or 2)
moving the call to buildStart for dev/serve and to generateBundle only for
actual builds (use the presence of this.getWatchMode() or hook semantics to
distinguish serve vs build); update the buildStart and generateBundle handlers
(the functions named buildStart and generateBundle) accordingly to ensure a
single invocation per process.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@context.md`:
- Line 82: Update the phrasing in the documentation string containing
Uniwind.setTheme(theme | 'system') to hyphenate the compound adjective: change
"system adaptive light/dark" to "system-adaptive light/dark" so the description
reads that Uniwind.setTheme switches explicit themes or returns to
system-adaptive light/dark.
- Line 117: The phrase in the documentation for uniwind(config) uses "pre Vite
plugin" and should be hyphenated; update the text that says "`uniwind(config)`
returns a pre Vite plugin." to read "`uniwind(config)` returns a pre-Vite
plugin." ensuring the compound adjective "pre-Vite" is hyphenated wherever this
wording appears.
- Around line 129-130: Update the phrasing describing scoped CSS variables to
use correct compound modifier hyphenation: change the phrase "theme and platform
scoped variables" (in the description of scopedVars) to "theme- and
platform-scoped variables" so that "platform-scoped" is hyphenated and the
shared "scoped" modifier is applied correctly to both "theme" and "platform".

---

Duplicate comments:
In `@packages/uniwind/src/bundler/adapters/metro/transformer.ts`:
- Around line 35-37: The path comparisons are brittle; normalize and resolve
both sides before matching: for the CSS check (isCss) resolve/normalize
config.uniwind.cssEntryFile against process.cwd() and resolve/normalize
projectRoot + filePath (or resolve filePath) before comparing, and for the
special file check resolve/normalize the target string (e.g.,
path.join('components','web','metro-injected.js')) and compare it against a
resolved/normalized filePath instead of using filePath.endsWith(...); update the
logic around the isCss computation and the if that references
'/components/web/metro-injected.js' to use these normalized/resolved paths so
comparisons work cross-platform and in monorepos.

In `@packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts`:
- Around line 24-26: The generated JS embeds scopedVarsName raw into a quoted
key which can break if the name contains `"` or `\`; update the
serializedScopedVars construction in compileNativeCSS.ts (the const
serializedScopedVars) to escape scopedVarsName before embedding (e.g., use
JSON.stringify(scopedVarsName) or explicitly replace `\` and `"` with `\\`/`\"`)
so the produced key is a valid JS string literal and cannot inject invalid code.

---

Nitpick comments:
In `@context.md`:
- Around line 157-158: Two consecutive sentences both starting with "Web
wrappers" reduce readability; rephrase one to vary sentence structure and
improve flow. For example, transform the first sentence that mentions mapping
into an active clause referencing the function name toRNWClassName (e.g.,
"toRNWClassName maps className to RNW CSS style markers") and rework the second
to lead with the outcome or purpose mentioning dataSet (e.g., "They also pass
the generated dataSet so data attribute variants can match"), or combine them
into a single sentence that includes both toRNWClassName and dataSet for
clarity.
- Around line 120-121: The three consecutive sentences that begin with "Vite"
(the lines stating "Vite uses Lightning CSS with `UniwindCSSVisitor`." and "Vite
generates artifacts on `buildStart` and `generateBundle`.") make the paragraph
repetitive; rephrase one or two of them to vary sentence openings and improve
flow (for example, start one sentence with the technology or a gerund/passive
phrase like "Using Lightning CSS, Vite..." or "Artifacts are generated during
`buildStart` and `generateBundle`"), or combine related ideas into a single
sentence to avoid repeating the subject.

In `@packages/uniwind/src/bundler/adapters/vite/vite.ts`:
- Around line 87-91: The plugin currently calls
bundlerConfig.generateArtifacts(cssArtifactPath) in both buildStart and
generateBundle causing duplicate work; modify the Vite hooks so artifacts are
generated only once per run by either 1) adding a module-scoped boolean flag
(e.g., artifactsGenerated) that buildStart and generateBundle check/set before
calling bundlerConfig.generateArtifacts(cssArtifactPath), or 2) moving the call
to buildStart for dev/serve and to generateBundle only for actual builds (use
the presence of this.getWatchMode() or hook semantics to distinguish serve vs
build); update the buildStart and generateBundle handlers (the functions named
buildStart and generateBundle) accordingly to ensure a single invocation per
process.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6a86f50-ca6f-4933-bcd7-4302fdb8f667

📥 Commits

Reviewing files that changed from the base of the PR and between d7e458f and 52ba715.

📒 Files selected for processing (83)
  • context.md
  • packages/uniwind/.oxlintrc.json
  • packages/uniwind/build.config.ts
  • packages/uniwind/src/bundler/adapters/metro/patches.ts
  • packages/uniwind/src/bundler/adapters/metro/resolvers.ts
  • packages/uniwind/src/bundler/adapters/metro/transformer.ts
  • packages/uniwind/src/bundler/adapters/vite/vite.ts
  • packages/uniwind/src/bundler/artifacts/css/extraUtilities.ts
  • packages/uniwind/src/bundler/css-compiler/compileCSS.ts
  • packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts
  • packages/uniwind/src/bundler/css-compiler/compileTailwind.ts
  • packages/uniwind/src/bundler/css-compiler/compileWebCSS.ts
  • packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts
  • packages/uniwind/src/bundler/css-processor/color.ts
  • packages/uniwind/src/bundler/css-processor/css.ts
  • packages/uniwind/src/bundler/css-processor/functions.ts
  • packages/uniwind/src/bundler/css-processor/mq.ts
  • packages/uniwind/src/bundler/css-processor/processor.ts
  • packages/uniwind/src/bundler/css-processor/types.ts
  • packages/uniwind/src/bundler/css-processor/units.ts
  • packages/uniwind/src/bundler/css-processor/var.ts
  • packages/uniwind/src/bundler/css-visitor/function-visitor.ts
  • packages/uniwind/src/bundler/css-visitor/rule-visitor.ts
  • packages/uniwind/src/bundler/css-visitor/visitor.ts
  • packages/uniwind/src/components/ScopedTheme/ScopedTheme.native.tsx
  • packages/uniwind/src/components/native/ActivityIndicator.tsx
  • packages/uniwind/src/components/native/Button.tsx
  • packages/uniwind/src/components/native/FlatList.tsx
  • packages/uniwind/src/components/native/Image.tsx
  • packages/uniwind/src/components/native/ImageBackground.tsx
  • packages/uniwind/src/components/native/InputAccessoryView.tsx
  • packages/uniwind/src/components/native/KeyboardAvoidingView.tsx
  • packages/uniwind/src/components/native/Modal.tsx
  • packages/uniwind/src/components/native/Pressable.tsx
  • packages/uniwind/src/components/native/RefreshControl.tsx
  • packages/uniwind/src/components/native/SafeAreaView.tsx
  • packages/uniwind/src/components/native/ScrollView.tsx
  • packages/uniwind/src/components/native/SectionList.tsx
  • packages/uniwind/src/components/native/Switch.tsx
  • packages/uniwind/src/components/native/Text.tsx
  • packages/uniwind/src/components/native/TextInput.tsx
  • packages/uniwind/src/components/native/TouchableHighlight.tsx
  • packages/uniwind/src/components/native/TouchableNativeFeedback.tsx
  • packages/uniwind/src/components/native/TouchableOpacity.tsx
  • packages/uniwind/src/components/native/TouchableWithoutFeedback.tsx
  • packages/uniwind/src/components/native/View.tsx
  • packages/uniwind/src/components/native/VirtualizedList.tsx
  • packages/uniwind/src/components/native/useAccentColor.ts
  • packages/uniwind/src/components/native/useStyle.ts
  • packages/uniwind/src/components/web/ActivityIndicator.tsx
  • packages/uniwind/src/components/web/Button.tsx
  • packages/uniwind/src/components/web/FlatList.tsx
  • packages/uniwind/src/components/web/Image.tsx
  • packages/uniwind/src/components/web/ImageBackground.tsx
  • packages/uniwind/src/components/web/KeyboardAvoidingView.tsx
  • packages/uniwind/src/components/web/Modal.tsx
  • packages/uniwind/src/components/web/Pressable.tsx
  • packages/uniwind/src/components/web/RefreshControl.tsx
  • packages/uniwind/src/components/web/SafeAreaView.tsx
  • packages/uniwind/src/components/web/ScrollView.tsx
  • packages/uniwind/src/components/web/SectionList.tsx
  • packages/uniwind/src/components/web/Switch.tsx
  • packages/uniwind/src/components/web/Text.tsx
  • packages/uniwind/src/components/web/TextInput.tsx
  • packages/uniwind/src/components/web/TouchableHighlight.tsx
  • packages/uniwind/src/components/web/TouchableOpacity.tsx
  • packages/uniwind/src/components/web/TouchableWithoutFeedback.tsx
  • packages/uniwind/src/components/web/View.tsx
  • packages/uniwind/src/components/web/VirtualizedList.tsx
  • packages/uniwind/src/core/config/config.common.ts
  • packages/uniwind/src/core/config/config.native.ts
  • packages/uniwind/src/core/config/config.ts
  • packages/uniwind/src/core/native/store.ts
  • packages/uniwind/src/core/types.ts
  • packages/uniwind/src/core/web/getWebStyles.ts
  • packages/uniwind/src/hoc/types.ts
  • packages/uniwind/src/hoc/withUniwind.native.tsx
  • packages/uniwind/src/hoc/withUniwind.tsx
  • packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts
  • packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts
  • packages/uniwind/src/hooks/useResolveClassNames.ts
  • packages/uniwind/src/hooks/useUniwind.ts
  • packages/uniwind/tsconfig.json
✅ Files skipped from review due to trivial changes (70)
  • packages/uniwind/src/components/native/useStyle.ts
  • packages/uniwind/src/components/native/useAccentColor.ts
  • packages/uniwind/src/components/web/ImageBackground.tsx
  • packages/uniwind/src/bundler/css-processor/units.ts
  • packages/uniwind/src/components/ScopedTheme/ScopedTheme.native.tsx
  • packages/uniwind/src/hooks/useResolveClassNames.ts
  • packages/uniwind/src/components/web/TextInput.tsx
  • packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts
  • packages/uniwind/src/bundler/css-processor/functions.ts
  • packages/uniwind/src/components/web/TouchableOpacity.tsx
  • packages/uniwind/src/components/native/ImageBackground.tsx
  • packages/uniwind/src/components/native/View.tsx
  • packages/uniwind/src/bundler/artifacts/css/extraUtilities.ts
  • packages/uniwind/src/components/native/ActivityIndicator.tsx
  • packages/uniwind/src/hoc/withUniwind.native.tsx
  • packages/uniwind/src/components/native/KeyboardAvoidingView.tsx
  • packages/uniwind/src/components/native/Button.tsx
  • packages/uniwind/src/components/native/TouchableWithoutFeedback.tsx
  • packages/uniwind/src/components/native/ScrollView.tsx
  • packages/uniwind/src/components/web/Image.tsx
  • packages/uniwind/src/components/web/View.tsx
  • packages/uniwind/src/components/web/VirtualizedList.tsx
  • packages/uniwind/src/bundler/css-visitor/visitor.ts
  • packages/uniwind/src/components/web/Button.tsx
  • packages/uniwind/src/hoc/types.ts
  • packages/uniwind/src/components/native/Image.tsx
  • packages/uniwind/src/components/native/TouchableOpacity.tsx
  • packages/uniwind/src/components/native/TouchableHighlight.tsx
  • packages/uniwind/src/components/web/KeyboardAvoidingView.tsx
  • packages/uniwind/src/components/native/VirtualizedList.tsx
  • packages/uniwind/src/components/native/Text.tsx
  • packages/uniwind/src/components/native/SectionList.tsx
  • packages/uniwind/src/core/native/store.ts
  • packages/uniwind/src/components/web/ScrollView.tsx
  • packages/uniwind/src/components/native/SafeAreaView.tsx
  • packages/uniwind/src/components/native/InputAccessoryView.tsx
  • packages/uniwind/src/hoc/withUniwind.tsx
  • packages/uniwind/src/bundler/css-processor/css.ts
  • packages/uniwind/src/bundler/css-processor/processor.ts
  • packages/uniwind/src/bundler/css-visitor/rule-visitor.ts
  • packages/uniwind/src/components/web/ActivityIndicator.tsx
  • packages/uniwind/src/components/web/Switch.tsx
  • packages/uniwind/src/bundler/css-processor/var.ts
  • packages/uniwind/src/components/web/TouchableWithoutFeedback.tsx
  • packages/uniwind/src/components/native/TouchableNativeFeedback.tsx
  • packages/uniwind/src/components/web/FlatList.tsx
  • packages/uniwind/src/components/native/FlatList.tsx
  • packages/uniwind/src/bundler/css-compiler/compileTailwind.ts
  • packages/uniwind/src/bundler/css-visitor/function-visitor.ts
  • packages/uniwind/src/components/web/RefreshControl.tsx
  • packages/uniwind/src/components/native/TextInput.tsx
  • packages/uniwind/src/components/native/Pressable.tsx
  • packages/uniwind/src/bundler/adapters/metro/patches.ts
  • packages/uniwind/src/components/web/SafeAreaView.tsx
  • packages/uniwind/src/components/web/SectionList.tsx
  • packages/uniwind/src/components/web/Modal.tsx
  • packages/uniwind/src/core/web/getWebStyles.ts
  • packages/uniwind/src/components/native/Modal.tsx
  • packages/uniwind/src/components/native/RefreshControl.tsx
  • packages/uniwind/src/core/config/config.common.ts
  • packages/uniwind/src/components/web/Pressable.tsx
  • packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts
  • packages/uniwind/.oxlintrc.json
  • packages/uniwind/src/core/config/config.ts
  • packages/uniwind/src/bundler/css-processor/mq.ts
  • packages/uniwind/src/hooks/useUniwind.ts
  • packages/uniwind/src/bundler/adapters/metro/resolvers.ts
  • packages/uniwind/src/components/native/Switch.tsx
  • packages/uniwind/src/components/web/TouchableHighlight.tsx
  • packages/uniwind/src/components/web/Text.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/uniwind/src/core/config/config.native.ts
  • packages/uniwind/src/bundler/css-compiler/compileCSS.ts
  • packages/uniwind/src/bundler/css-processor/color.ts
  • packages/uniwind/src/bundler/css-processor/types.ts
  • packages/uniwind/src/bundler/css-compiler/compileWebCSS.ts
  • packages/uniwind/src/core/types.ts
  • packages/uniwind/build.config.ts
  • packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts

Comment thread context.md Outdated
Comment thread context.md Outdated
Comment thread context.md Outdated
@Brentlok Brentlok changed the title Major bundler refactor [WIP] refactor: major bundler refactor May 18, 2026
@Brentlok Brentlok merged commit 6649456 into main May 18, 2026
2 checks passed
@Brentlok Brentlok deleted the major-refactor branch May 18, 2026 09:00
@github-actions

Copy link
Copy Markdown
Contributor

🚀 This pull request is included in v1.7.0. See Release v1.7.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.

1 participant