Skip to content

feat: add support for variables in media queries#335

Closed
divineniiquaye wants to merge 2 commits into
uni-stack:mainfrom
divineniiquaye:media-queries
Closed

feat: add support for variables in media queries#335
divineniiquaye wants to merge 2 commits into
uni-stack:mainfrom
divineniiquaye:media-queries

Conversation

@divineniiquaye

@divineniiquaye divineniiquaye commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

fixes #242

Summary by CodeRabbit

  • New Features

    • CSS variables now support media queries, enabling responsive styling based on screen size breakpoints, device orientation, and color-scheme preferences.
  • Tests

    • Added comprehensive test coverage for media-query-driven CSS variable resolution across multiple scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds support for CSS variables inside media queries in UniWind. It introduces recursive media query condition processing in the compiler and defers variable resolution for media-query-backed declarations, resolving them after all style rules are processed. Comprehensive test coverage validates responsive typography and styling across multiple media query scenarios.

Changes

Cohort / File(s) Summary
Core processor logic
packages/uniwind/src/metro/processor/mq.ts
Added processCondition helper method to recursively traverse and process nested media query conditions (operation type) and feature-based conditions (range/plain values). Replaced inline early-return logic with centralized recursive traversal.
Core processor refactoring
packages/uniwind/src/metro/processor/processor.ts
Added varsWithMediaQueries and pendingVarReferences fields to defer variable declaration processing; introduced parseDeclaration helper for normalized declaration parsing; added hasMediaQuery detection; routes media-query-backed variables to storage path and splices them into class styles after all rules are processed; broadened media-query field range to include maxWidth, minWidth, orientation.
Type safety improvements
packages/uniwind/src/metro/types.ts
Tightened MediaQueryResolver type: changed maxWidth and minWidth from any to number | null for stricter type safety.
CSS variable + color-scheme tests
packages/uniwind/tests/media-queries/color-scheme.test.css, color-scheme.test.tsx
New test suite validating CSS variable-based color schemes for light and dark themes; verifies default values and media-query-driven color overrides via prefers-color-scheme.
CSS variable + combined media query tests
packages/uniwind/tests/media-queries/combined.test.css, combined.test.tsx
New test suite for theme layers and responsive CSS variables; tests combinations of min-width, orientation, and color-scheme media queries applied to --text-xl and --border-width variables.
CSS variable + max-width tests
packages/uniwind/tests/media-queries/max-width.test.css, max-width.test.tsx
New test suite validating max-width media query behavior on CSS variables; includes dynamic screen width changes and threshold crossing scenarios.
CSS variable + min-width tests
packages/uniwind/tests/media-queries/min-width.test.css, min-width.test.tsx
New test suite for min-width breakpoints applied to CSS variables; validates correct variable resolution across multiple breakpoints (320px, 640px, 1024px) and dynamic screen width updates.
CSS variable + orientation tests
packages/uniwind/tests/media-queries/orientation.test.css, orientation.test.tsx
New test suite for orientation-based CSS variable overrides; verifies default portrait values and landscape overrides, dynamic orientation changes, and condition precedence.
CSS variable + baseline tests
packages/uniwind/tests/media-queries/variables.test.css, variables.test.tsx
New test suite for foundational CSS variable media query support; validates min-width thresholds, dynamic screen width changes, and fallback values.

Sequence Diagram

sequenceDiagram
    participant Compiler as Style Compiler
    participant ProcBuilder as ProcessorBuilder
    participant MqProc as MediaQuery Processor
    participant VarStore as Variable Storage
    participant Finalizer as Style Finalizer

    Compiler->>ProcBuilder: process stylesheet
    ProcBuilder->>ProcBuilder: iterate style rules
    
    ProcBuilder->>MqProc: processCondition(condition)
    MqProc->>MqProc: check condition.type
    alt operation type
        MqProc->>MqProc: recursively process nested
    else feature type
        MqProc->>ProcBuilder: process range/plain values
    end
    
    ProcBuilder->>ProcBuilder: hasMediaQuery check
    alt media query exists
        ProcBuilder->>VarStore: store in varsWithMediaQueries
        ProcBuilder->>ProcBuilder: track in pendingVarReferences
    else no media query
        ProcBuilder->>ProcBuilder: assign style directly
    end
    
    ProcBuilder->>ProcBuilder: finalize all rules
    ProcBuilder->>Finalizer: iterate pendingVarReferences
    Finalizer->>VarStore: retrieve varsWithMediaQueries
    Finalizer->>ProcBuilder: splice into class styles
    Finalizer->>ProcBuilder: clear pending references
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


🐰 CSS variables hop through media queries now,
No more flat styles, they bloom and grow!
Responsive fonts dance at each breakpoint's call,
Portrait to landscape, they scale through it all. ✨📱

🚥 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 PR title 'feat: add support for variables in media queries' directly reflects the main change - implementing support for CSS variables declared within media queries.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #242: handling CSS variables inside media queries, enabling responsive typography through media-query-based variable redefinition.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing media query support for CSS variables. Core logic updates in processor files, type refinements, and comprehensive test coverage all align with the stated objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@divineniiquaye divineniiquaye changed the title feat: add support for media queries feat: add support for variables in media queries Jan 24, 2026

@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: 4

Caution

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

⚠️ Outside diff range comments (3)
packages/uniwind/src/metro/processor/processor.ts (2)

83-149: Track all CSS var references (arrays/objects + multiple matches).
Right now only the first this[...] in a top‑level string is detected. Vars embedded in arrays/objects (e.g., transforms, shadows) or multiple vars in one value won’t be tracked, so media‑query var updates won’t be injected for those classes.

🐛 Proposed fix
-        const match = typeof value === 'string' ? value.match(/this\[`(.*?)`\]/) : null
-
-        if (match && !isVar) {
+        const varRefs = !isVar ? this.extractVarRefs(value) : []
+        if (varRefs.length > 0) {
             const className = this.declarationConfig.className
             if (className === null) {
                 return
             }
@@
-            const classVars = this.pendingVarReferences.get(className)!
-            const varName = match[1]!
-
-            if (!classVars.includes(varName)) {
-                classVars.push(varName)
-            }
+            const classVars = this.pendingVarReferences.get(className)!
+            for (const varName of varRefs) {
+                if (!classVars.includes(varName)) {
+                    classVars.push(varName)
+                }
+            }
         }

Add a helper (near parseDeclaration):

private extractVarRefs(value: unknown): Array<string> {
    const refs = new Set<string>()
    const visit = (v: any) => {
        if (typeof v === 'string') {
            for (const m of v.matchAll(/this\[(?:`|"|')([^`"']+)(?:`|"|')\]/g)) {
                if (m[1]?.startsWith('--')) refs.add(m[1])
            }
            return
        }
        if (Array.isArray(v)) return v.forEach(visit)
        if (v && typeof v === 'object') Object.values(v).forEach(visit)
    }
    visit(value)
    return [...refs]
}

274-279: Preserve outer declaration context for nested @media.
Resetting to getDeclarationConfig() here drops any outer media context when @media is nested (including CSS nesting). That can cause subsequent rules in the outer block to lose their media constraints.

🐛 Proposed fix
-            this.declarationConfig.mediaQueries.push(...mediaQueries)
-            rule.value.rules.forEach(rule => this.parseRuleRec(rule))
-            this.declarationConfig = this.getDeclarationConfig()
+            const prevConfig = this.declarationConfig
+            this.declarationConfig = {
+                ...prevConfig,
+                mediaQueries: [...prevConfig.mediaQueries, ...mediaQueries],
+            }
+            rule.value.rules.forEach(rule => this.parseRuleRec(rule))
+            this.declarationConfig = prevConfig
packages/uniwind/src/metro/processor/mq.ts (1)

1-40: Handle or and not operators in media conditions to apply styles correctly.

processCondition does not check the operator field in 'operation' type conditions, treating or as and (all conditions applied to the same resolver). It also ignores the 'not' condition type entirely. This causes or operations to incorrectly apply all branches simultaneously and not conditions to be silently skipped. For example, (max-width: 600px or min-width: 1200px) would incorrectly set both constraints.

Either split or operations into multiple resolvers, or detect and explicitly handle/reject unsupported operators.

🤖 Fix all issues with AI agents
In `@packages/uniwind/tests/media-queries/color-scheme.test.tsx`:
- Around line 26-29: Replace the eval(...) call that invokes Uniwind.__reinit
with a new Function(...) pattern and add the same linting disable/enable
comments used in production tests so the test passes linting; specifically
change the eval invocation that builds and executes the runtime string for
Uniwind.__reinit(rt => ${virtualCode}, ['light','dark']) to use new
Function(...) to return and call the function, and wrap that call with the same
eslint-disable/enable comments used elsewhere in tests; apply the identical
change to the other test files noted (variables, orientation, min-width,
max-width, combined, setup.ts) so they follow the consistent pattern.

In `@packages/uniwind/tests/media-queries/combined.test.tsx`:
- Around line 25-28: Replace the eval(...) call that constructs and runs the
reinit code for Uniwind.__reinit with a new Function(...) invocation (so you no
longer use eval) and ensure you add the same eslint-disable comment pattern used
elsewhere (see functions.ts/common.ts/serialize.ts) immediately above the new
Function call to suppress the linter rule for creating functions dynamically;
keep the Uniwind.__reinit(...) invocation and the virtualCode interpolation
logic intact while swapping the eval to new Function to avoid eval usage.

In `@packages/uniwind/tests/media-queries/max-width.test.tsx`:
- Around line 26-29: Replace the eval(...) wrapper around the string that
invokes Uniwind.__reinit by creating a new Function that takes the runtime (rt)
and the generated virtualCode and invokes Uniwind.__reinit; specifically, stop
calling eval with the template string and instead use the Function constructor
to build a callable function that receives rt and virtualCode and executes the
same body, then call that function with rt and the virtualCode variable so
Uniwind.__reinit is invoked without using eval.

In `@packages/uniwind/tests/media-queries/orientation.test.tsx`:
- Around line 14-30: Replace the global eval usage in beforeAll by providing a
real function initializer to Uniwind.__reinit instead of eval'ing the string in
virtualCode: change compileVirtual (or its caller) to return a callable (e.g., a
function that accepts rt and returns the virtual object) rather than JS source,
then call Uniwind.__reinit(initializerFn, ['light','dark']) directly; update the
test to require('../../src/core/config/config.native') to get Uniwind and pass
the returned function (not a string) into Uniwind.__reinit, removing the eval
and satisfying lint/security/noGlobalEval while keeping the same behavior for
virtualCode and themes.
♻️ Duplicate comments (2)
packages/uniwind/tests/media-queries/variables.test.tsx (1)

14-30: Duplicate: global eval in test setup.

Same issue as in packages/uniwind/tests/media-queries/orientation.test.tsx; please apply the same refactor or suppression here.

packages/uniwind/tests/media-queries/min-width.test.tsx (1)

14-30: Duplicate: global eval in test setup.

Same issue as in packages/uniwind/tests/media-queries/orientation.test.tsx; please apply the same refactor or suppression here.

Comment on lines +26 to +29
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd packages/uniwind/tests/media-queries && wc -l color-scheme.test.tsx

Repository: uni-stack/uniwind

Length of output: 85


🏁 Script executed:

cat -n packages/uniwind/tests/media-queries/color-scheme.test.tsx | head -50

Repository: uni-stack/uniwind

Length of output: 2334


🏁 Script executed:

# Check if there's a Biome configuration in the repo
find . -name "biome.json" -o -name ".biomeignore" -o -name "biome.jsonc" | head -5

Repository: uni-stack/uniwind

Length of output: 73


🏁 Script executed:

# Search for eval usage patterns in the test file and check context
rg "eval\(" packages/uniwind/tests/media-queries/color-scheme.test.tsx -A 5 -B 5

Repository: uni-stack/uniwind

Length of output: 375


🏁 Script executed:

cat biome.json

Repository: uni-stack/uniwind

Length of output: 1835


🏁 Script executed:

# Check for security/eval rules in Biome config
rg "noEval|eval|security" biome.json -A 3 -B 3

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Check the actual Uniwind.__reinit signature to understand if the proposed fix is compatible
rg "__reinit" packages/uniwind/src -A 10 -B 2

Repository: uni-stack/uniwind

Length of output: 5015


🏁 Script executed:

# Check if Biome's recommended rules include noEval
rg "noEval|eval" . --type json | head -20

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Verify the actual type signature of GenerateStyleSheetsCallback
rg "GenerateStyleSheetsCallback" packages/uniwind/src -B 2 -A 5

Repository: uni-stack/uniwind

Length of output: 5399


🏁 Script executed:

# Check test setup to understand what virtualCode contains
cd packages/uniwind && rg "compileVirtual" src -A 3 -B 1

Repository: uni-stack/uniwind

Length of output: 1118


🏁 Script executed:

# Check Biome's documentation for noEval rule behavior with new Function
web_search "Biome noEval rule new Function"

Repository: uni-stack/uniwind

Length of output: 111


🏁 Script executed:

# Verify what virtualCode actually contains by checking compileVirtual return type
cat -n packages/uniwind/src/metro/compileVirtual.ts | head -100

Repository: uni-stack/uniwind

Length of output: 3037


🏁 Script executed:

# Look for other test patterns in the codebase to see how they handle Uniwind.__reinit
rg "__reinit" packages/uniwind/tests -B 5 -A 5

Repository: uni-stack/uniwind

Length of output: 6109


🏁 Script executed:

# Check Biome's recommended rules documentation by looking at their website
# For now, let's verify if there are any lint errors currently
rg "noEval" . --type json

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Let's verify the proposed fix by checking if new Function is also commonly flagged
rg "new Function" packages/uniwind -B 3 -A 3

Repository: uni-stack/uniwind

Length of output: 2416


🏁 Script executed:

# Check if this eval pattern appears in production code or only in tests
rg "eval\(" packages/uniwind/src -B 2 -A 2

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Verify that virtualCode doesn't contain any external variable references
# by looking at what serializeJSObject returns
cat -n packages/uniwind/src/metro/utils.ts | head -50

Repository: uni-stack/uniwind

Length of output: 128


🏁 Script executed:

# Check if there's any Biome-specific comment/ignore for eval in the codebase
rg "biome" packages/uniwind/tests -i

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Let's verify the exact structure of what virtualCode produces by looking at a real test output
# Check what the expected object structure is
rg "GenerateStyleSheetsCallback" packages/uniwind/src/core/types.ts -A 10

Repository: uni-stack/uniwind

Length of output: 417


🏁 Script executed:

# Verify if the proposed new Function approach would maintain scope correctly
# by checking the actual signature expected
cat -n packages/uniwind/src/core/types.ts | head -50

Repository: uni-stack/uniwind

Length of output: 1776


🏁 Script executed:

# Search for ESLint/Biome disable comments related to eval or function
rg "eslint-disable|biome-disable" packages/uniwind -i

Repository: uni-stack/uniwind

Length of output: 1871


🏁 Script executed:

# Verify that new Function is also flagged by checking if disable comments are necessary
# by looking at whether Biome has a rule equivalent to ESLint's no-new-func
web_search Biome linter security rules eval new Function recommended

Repository: uni-stack/uniwind

Length of output: 111


🏁 Script executed:

# Check if there are any test utilities that could help avoid eval/new Function entirely
rg "renderUniwind\|UniwindListener\|UniwindStore" packages/uniwind/tests -l | head -5

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Verify if the pattern requires a function factory or if there's a way to directly set the callback
cat -n packages/uniwind/src/core/native/store.ts | sed -n '1,100p'

Repository: uni-stack/uniwind

Length of output: 4389


🏁 Script executed:

# Check what the actual Biome/ESLint config expects for new Function usage
rg "no-new-func|no-implied-eval" packages/uniwind/.eslintrc -A 2 -B 2 2>/dev/null || echo "No .eslintrc found"

Repository: uni-stack/uniwind

Length of output: 79


🏁 Script executed:

# Verify if .eslintignore exists for tests
find packages/uniwind -name ".eslintignore" -o -name ".eslintrc*" | xargs cat 2>/dev/null | head -30

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Check if there's a tsconfig or jest config that might affect linting
cat packages/uniwind/jest.config.js 2>/dev/null | head -50 || cat packages/uniwind/tsconfig.json 2>/dev/null | head -30

Repository: uni-stack/uniwind

Length of output: 418


🏁 Script executed:

# Check if there are Biome ignore comment examples in the codebase
rg "biome-ignore" packages/uniwind

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Verify if Biome is actually being used for linting these test files
rg "ignorePatterns\|\"test\"\|\"tests\"" biome.json

Repository: uni-stack/uniwind

Length of output: 43


Use new Function() instead of eval() to avoid linting failure.

The proposed fix is functionally correct, but requires linting disable comments to match the production code pattern. Update to:

🔧 Proposed fix (with linting disables)
+        // eslint-disable-next-line `@typescript-eslint/no-implied-eval`, no-new-func
+        const { Uniwind } = require('../../src/core/config/config.native')
+        const factory = new Function('rt', `return ${virtualCode}`)
+        Uniwind.__reinit(factory, ['light', 'dark'])
-        eval(
-            `const { Uniwind } = require('../../src/core/config/config.native');
-            Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
-        `,
-        )

Note: This pattern appears in 6 other test files (variables, orientation, min-width, max-width, combined, setup.ts) that should be updated consistently.

🧰 Tools
🪛 Biome (2.1.2)

[error] 26-26: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In `@packages/uniwind/tests/media-queries/color-scheme.test.tsx` around lines 26 -
29, Replace the eval(...) call that invokes Uniwind.__reinit with a new
Function(...) pattern and add the same linting disable/enable comments used in
production tests so the test passes linting; specifically change the eval
invocation that builds and executes the runtime string for Uniwind.__reinit(rt
=> ${virtualCode}, ['light','dark']) to use new Function(...) to return and call
the function, and wrap that call with the same eslint-disable/enable comments
used elsewhere in tests; apply the identical change to the other test files
noted (variables, orientation, min-width, max-width, combined, setup.ts) so they
follow the consistent pattern.

Comment on lines +25 to +28
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd "combined.test.tsx" --type f

Repository: uni-stack/uniwind

Length of output: 115


🏁 Script executed:

if [ -f "packages/uniwind/tests/media-queries/combined.test.tsx" ]; then
  wc -l "packages/uniwind/tests/media-queries/combined.test.tsx"
fi

Repository: uni-stack/uniwind

Length of output: 119


🏁 Script executed:

cat -n "packages/uniwind/tests/media-queries/combined.test.tsx" | head -50

Repository: uni-stack/uniwind

Length of output: 2361


🏁 Script executed:

rg "eval\(" packages/uniwind/tests --type tsx -B 2 -A 2

Repository: uni-stack/uniwind

Length of output: 88


🏁 Script executed:

cat -n "packages/uniwind/tests/media-queries/combined.test.tsx" | sed -n '12,30p'

Repository: uni-stack/uniwind

Length of output: 835


🏁 Script executed:

# Check what compileVirtual returns and how Uniwind.__reinit is used
rg "compileVirtual" packages/uniwind/tests --type tsx -B 1 -A 1 | head -40

Repository: uni-stack/uniwind

Length of output: 88


🏁 Script executed:

# Look at other test files to understand the pattern
fd "\.test\.tsx$" packages/uniwind/tests --type f

Repository: uni-stack/uniwind

Length of output: 2215


🏁 Script executed:

grep -r "eval(" packages/uniwind/tests --include="*.tsx" -B 2 -A 2

Repository: uni-stack/uniwind

Length of output: 2843


🏁 Script executed:

# Check if there are other similar patterns in different test files
grep -r "__reinit" packages/uniwind/tests --include="*.tsx" -B 3 -A 3

Repository: uni-stack/uniwind

Length of output: 3625


🏁 Script executed:

# Examine the config.native file to understand __reinit signature
cat -n "packages/uniwind/src/core/config/config.native.ts" | head -60

Repository: uni-stack/uniwind

Length of output: 2574


🏁 Script executed:

# Check for Function constructor usage in the codebase
grep -r "new Function" packages/uniwind --include="*.ts" --include="*.tsx" -B 1 -A 1

Repository: uni-stack/uniwind

Length of output: 1238


🏁 Script executed:

grep -A 10 "__reinit" packages/uniwind/src/core/config/config.native.ts | head -20

Repository: uni-stack/uniwind

Length of output: 474


🏁 Script executed:

# Get more context about __reinit signature
grep -B 5 -A 15 "reinit.*=" packages/uniwind/src/core/config/config.native.ts | head -40

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

# Let's look at the full __reinit method
rg "__reinit" packages/uniwind/src/core/config --type ts -B 2 -A 10

Repository: uni-stack/uniwind

Length of output: 2014


🏁 Script executed:

# Check what compileVirtual returns - is it an expression or statements?
cat -n packages/uniwind/src/metro/compileVirtual.ts | head -40

Repository: uni-stack/uniwind

Length of output: 1602


🏁 Script executed:

# Verify the return type of compileVirtual
rg "compileVirtual.*=" packages/uniwind/src/metro --type ts -B 2 -A 5 | head -30

Repository: uni-stack/uniwind

Length of output: 709


🏁 Script executed:

# Check if require can be called inside beforeAll (verify the proposed fix viability)
rg "beforeAll" packages/uniwind/tests -A 10 --type js | grep "require" | head -5

Repository: uni-stack/uniwind

Length of output: 43


🏁 Script executed:

cat -n packages/uniwind/src/metro/compileVirtual.ts | tail -30

Repository: uni-stack/uniwind

Length of output: 1369


🏁 Script executed:

# Look at the full function to understand return type
sed -n '21,80p' packages/uniwind/src/metro/compileVirtual.ts

Repository: uni-stack/uniwind

Length of output: 1905


🏁 Script executed:

# Check how the virtualCode is being used - is it a function body or complete code?
cat -n "packages/uniwind/tests/media-queries/combined.test.tsx" | sed -n '13,30p'

Repository: uni-stack/uniwind

Length of output: 769


🏁 Script executed:

# Verify the proposed fix by checking the GenerateStyleSheetsCallback type signature
rg "GenerateStyleSheetsCallback" packages/uniwind/src/core --type ts -B 2 -A 2

Repository: uni-stack/uniwind

Length of output: 3758


🏁 Script executed:

# Test if the proposed new Function approach would work with the compiled virtualCode
python3 << 'EOF'
# Simulate what compileVirtual returns and what both eval and new Function would produce
virtualCode = '({ scopedVars: ({}), vars: ({}), stylesheet: ({}) })'

# Original eval approach (simulated)
eval_code = f"rt => {virtualCode}"
print("Eval creates arrow function: rt => " + virtualCode)
print()

# Proposed new Function approach
proposed_code = f"return {virtualCode}"
print("new Function creates function with body: " + proposed_code)
print()

# Both should create functions that take 'rt' param and return the same object
print("Both approaches create callable functions with signature: (rt) => object")
print("Semantics are equivalent ✓")
EOF

Repository: uni-stack/uniwind

Length of output: 147


🏁 Script executed:

# Check if there are linting configurations that flag eval
find packages/uniwind -name "eslint*" -o -name ".eslintrc*" | xargs cat 2>/dev/null | grep -A 5 -B 5 "eval" || echo "No eval-specific eslint rules found in basic search"

Repository: uni-stack/uniwind

Length of output: 112


🏁 Script executed:

# Verify similar patterns exist in other test files to confirm pattern
cat -n packages/uniwind/tests/media-queries/color-scheme.test.tsx | sed -n '24,30p'

Repository: uni-stack/uniwind

Length of output: 308


Avoid eval() here as well; same lint/security concern.

The proposed fix with new Function() is correct, but it needs the eslint-disable comment to match the pattern used elsewhere in the codebase (see functions.ts, common.ts, serialize.ts).

🔧 Proposed fix (no eval)
-        eval(
-            `const { Uniwind } = require('../../src/core/config/config.native');
-            Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
-        `,
-        )
+        const { Uniwind } = require('../../src/core/config/config.native')
+        // eslint-disable-next-line `@typescript-eslint/no-implied-eval`, no-new-func
+        const factory = new Function('rt', `return ${virtualCode}`)
+        Uniwind.__reinit(factory, ['light', 'dark'])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,
const { Uniwind } = require('../../src/core/config/config.native')
// eslint-disable-next-line `@typescript-eslint/no-implied-eval`, no-new-func
const factory = new Function('rt', `return ${virtualCode}`)
Uniwind.__reinit(factory, ['light', 'dark'])
🧰 Tools
🪛 Biome (2.1.2)

[error] 25-25: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In `@packages/uniwind/tests/media-queries/combined.test.tsx` around lines 25 - 28,
Replace the eval(...) call that constructs and runs the reinit code for
Uniwind.__reinit with a new Function(...) invocation (so you no longer use eval)
and ensure you add the same eslint-disable comment pattern used elsewhere (see
functions.ts/common.ts/serialize.ts) immediately above the new Function call to
suppress the linter rule for creating functions dynamically; keep the
Uniwind.__reinit(...) invocation and the virtualCode interpolation logic intact
while swapping the eval to new Function to avoid eval usage.

Comment on lines +26 to +29
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,

Copy link
Copy Markdown

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:

cat -n packages/uniwind/tests/media-queries/max-width.test.tsx | sed -n '20,35p'

Repository: uni-stack/uniwind

Length of output: 666


🏁 Script executed:

fd -t f -e json -e js -e yml -e yaml "eslint|biome|lint" | head -20

Repository: uni-stack/uniwind

Length of output: 71


🏁 Script executed:

git ls-files | grep -E "(eslint|biome|lint)" | head -20

Repository: uni-stack/uniwind

Length of output: 94


🏁 Script executed:

cat biome.json

Repository: uni-stack/uniwind

Length of output: 1835


🏁 Script executed:

cat packages/uniwind/eslint.config.ts

Repository: uni-stack/uniwind

Length of output: 1180


🏁 Script executed:

rg -t ts "eval" packages/uniwind/tests/media-queries/max-width.test.tsx -B 3 -A 3

Repository: uni-stack/uniwind

Length of output: 283


🏁 Script executed:

git ls-files | grep -E "\.(yml|yaml|json)" | xargs grep -l "biome\|lint" | head -10

Repository: uni-stack/uniwind

Length of output: 171


🏁 Script executed:

cat .github/workflows/ci.yaml

Repository: uni-stack/uniwind

Length of output: 860


🏁 Script executed:

cat package.json | grep -A 20 '"scripts"'

Repository: uni-stack/uniwind

Length of output: 729


🏁 Script executed:

cat packages/uniwind/package.json | grep -A 20 '"scripts"'

Repository: uni-stack/uniwind

Length of output: 710


🏁 Script executed:

rg "biome" .github/workflows/ turbo.json package.json

Repository: uni-stack/uniwind

Length of output: 43


Replace eval() with Function constructor for improved security and readability.

The eval() call is not flagged by the current linting setup (tests directory is excluded from ESLint), but removing it is still a best practice that reduces security risks and improves maintainability.

🔧 Proposed fix (no eval)
-        eval(
-            `const { Uniwind } = require('../../src/core/config/config.native');
-            Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
-        `,
-        )
+        const { Uniwind } = require('../../src/core/config/config.native')
+        const factory = new Function('rt', `return ${virtualCode}`)
+        Uniwind.__reinit(factory, ['light', 'dark'])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,
const { Uniwind } = require('../../src/core/config/config.native')
const factory = new Function('rt', `return ${virtualCode}`)
Uniwind.__reinit(factory, ['light', 'dark'])
🧰 Tools
🪛 Biome (2.1.2)

[error] 26-26: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In `@packages/uniwind/tests/media-queries/max-width.test.tsx` around lines 26 -
29, Replace the eval(...) wrapper around the string that invokes
Uniwind.__reinit by creating a new Function that takes the runtime (rt) and the
generated virtualCode and invokes Uniwind.__reinit; specifically, stop calling
eval with the template string and instead use the Function constructor to build
a callable function that receives rt and virtualCode and executes the same body,
then call that function with rt and the virtualCode variable so Uniwind.__reinit
is invoked without using eval.

Comment on lines +14 to +30
beforeAll(async () => {
const cssPath = resolve('./tests/media-queries/orientation.test.css')
const css = readFileSync(cssPath, 'utf-8')
const virtualCode = await compileVirtual({
css,
cssPath,
debug: true,
platform: Platform.iOS,
themes: ['light', 'dark'],
polyfills: undefined,
})

eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove global eval to satisfy lint/security rules.

Biome flags this as lint/security/noGlobalEval, which can fail CI. Consider constructing the initializer without global eval. One option is to compile a function that accepts rt and returns the virtual object.

🔧 Suggested refactor (avoid global eval)
-        eval(
-            `const { Uniwind } = require('../../src/core/config/config.native');
-            Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
-        `,
-        )
+        const { Uniwind } = require('../../src/core/config/config.native')
+        const buildVirtual = new Function('rt', `return ${virtualCode}`)
+        Uniwind.__reinit(rt => buildVirtual(rt), ['light', 'dark'])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
beforeAll(async () => {
const cssPath = resolve('./tests/media-queries/orientation.test.css')
const css = readFileSync(cssPath, 'utf-8')
const virtualCode = await compileVirtual({
css,
cssPath,
debug: true,
platform: Platform.iOS,
themes: ['light', 'dark'],
polyfills: undefined,
})
eval(
`const { Uniwind } = require('../../src/core/config/config.native');
Uniwind.__reinit(rt => ${virtualCode}, ['light', 'dark']);
`,
)
beforeAll(async () => {
const cssPath = resolve('./tests/media-queries/orientation.test.css')
const css = readFileSync(cssPath, 'utf-8')
const virtualCode = await compileVirtual({
css,
cssPath,
debug: true,
platform: Platform.iOS,
themes: ['light', 'dark'],
polyfills: undefined,
})
const { Uniwind } = require('../../src/core/config/config.native')
const buildVirtual = new Function('rt', `return ${virtualCode}`)
Uniwind.__reinit(rt => buildVirtual(rt), ['light', 'dark'])
🧰 Tools
🪛 Biome (2.1.2)

[error] 26-26: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In `@packages/uniwind/tests/media-queries/orientation.test.tsx` around lines 14 -
30, Replace the global eval usage in beforeAll by providing a real function
initializer to Uniwind.__reinit instead of eval'ing the string in virtualCode:
change compileVirtual (or its caller) to return a callable (e.g., a function
that accepts rt and returns the virtual object) rather than JS source, then call
Uniwind.__reinit(initializerFn, ['light','dark']) directly; update the test to
require('../../src/core/config/config.native') to get Uniwind and pass the
returned function (not a string) into Uniwind.__reinit, removing the eval and
satisfying lint/security/noGlobalEval while keeping the same behavior for
virtualCode and themes.

@Brentlok

Copy link
Copy Markdown
Contributor

As mentioned in the linked issue #242 , we are currently heads-down on ScopedThemes and won't be able to give this the thorough review.

To keep our active PR queue manageable and focused on the current release, I’m going to go ahead and close this for now. Please note: This is not a rejection. We simply close PRs that are on hold to keep our workspace clean.

@Brentlok Brentlok closed this Feb 13, 2026
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.

CSS variables inside media queries are not respected by UniWind

2 participants