## Summary
Fixes#20689
Previously, `@layer` blocks were not being processed through the CSS
minifier, which meant that `color-scheme` properties inside `@layer`
blocks would not get the required `--buncss-light`/`--buncss-dark`
variable injections needed for browsers that don't support the
`light-dark()` function.
## Changes
- Implemented proper minification for `LayerBlockRule` in
`src/css/rules/rules.zig:218-221`
- Added recursive call to `minify()` on nested rules, matching the
behavior of other at-rules like `@media` and `@supports`
- Added comprehensive tests for `color-scheme` inside `@layer` blocks
## Test Plan
Added three new test cases in `test/js/bun/css/css.test.ts`:
1. Simple `@layer` with `color-scheme: dark`
2. Named layers (`@layer shm.colors`) with multiple rules
3. Anonymous `@layer` with `color-scheme: light dark` (generates media
query)
All tests pass:
```bash
bun bd test test/js/bun/css/css.test.ts -t "color-scheme"
```
## Before
```css
/* Input */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
/* Output (broken - no variables) */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
```
## After
```css
/* Input */
@layer shm.colors {
body.theme-dark {
color-scheme: dark;
}
}
/* Output (fixed - variables injected) */
@layer shm.colors {
body.theme-dark {
--buncss-light: ;
--buncss-dark: initial;
color-scheme: dark;
}
}
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
### How did you verify your code works?
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Previously, handleOom(anyerror!T) would return T and panic for
OutOfMemory for any error. fixes it to return anyerror!T for this case.
### How did you verify your code works?
CI
---------
Co-authored-by: taylor.fish <contact@taylor.fish>
Replace `catch bun.outOfMemory()`, which can accidentally catch
non-OOM-related errors, with either `bun.handleOom` or a manual `catch
|err| switch (err)`.
(For internal tracking: fixes STAB-1070)
---------
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
## 🐛 Problem
Fixes#21907 - CSS parser was crashing with "integer part of floating
point value out of bounds" when processing extremely large
floating-point values like `3.40282e38px` (commonly generated by
TailwindCSS `.rounded-full` class).
### Root Cause Analysis
**This revealed a broader systemic issue**: The CSS parser was ported
from Rust, which has different float→integer conversion semantics than
Zig's `@intFromFloat`.
**Zig behavior**: `@intFromFloat` panics on out-of-range values
**Rust behavior**: `as` operator follows safe conversion rules:
- Finite values within range: truncate toward zero
- NaN: becomes 0
- Positive infinity: becomes target max value
- Negative infinity: becomes target min value
- Out-of-range finite values: clamp to target range
The crash occurred throughout the CSS codebase wherever `@intFromFloat`
was used, not just in the original failing location.
## 🔧 Comprehensive Solution
### 1. New Generic `bun.intFromFloat` Function
Created a reusable function in `src/bun.zig` that implements
Rust-compatible conversion semantics:
```zig
pub fn intFromFloat(comptime Int: type, value: anytype) Int {
// Handle NaN -> 0
if (std.math.isNan(value)) return 0;
// Handle infinities -> min/max bounds
if (std.math.isPositiveInf(value)) return std.math.maxInt(Int);
if (std.math.isNegativeInf(value)) return std.math.minInt(Int);
// Handle out-of-range values -> clamp to bounds
const min_float = @as(Float, @floatFromInt(std.math.minInt(Int)));
const max_float = @as(Float, @floatFromInt(std.math.maxInt(Int)));
if (value > max_float) return std.math.maxInt(Int);
if (value < min_float) return std.math.minInt(Int);
// Safe conversion for in-range values
return @as(Int, @intFromFloat(value));
}
```
### 2. Systematic Replacement Across CSS Codebase
Replaced **all 18 instances** of `@intFromFloat` in `src/css/` with
`bun.intFromFloat`:
| File | Conversions | Purpose |
|------|-------------|---------|
| `css_parser.zig` | 2 × `i32` | CSS dimension serialization |
| `css_internals.zig` | 9 × `u32` | Browser target version parsing |
| `values/color.zig` | 4 × `u8` | Color component conversion |
| `values/color_js.zig` | 1 × `i64→u8` | Alpha channel processing |
| `values/percentage.zig` | 1 × `i32` | Percentage value handling |
| `properties/custom.zig` | 1 × `i32` | Color helper function |
### 3. Comprehensive Test Coverage
- **New test suite**: `test/internal/int_from_float.test.ts` with inline
snapshots
- **Enhanced regression test**: `test/regression/issue/21907.test.ts`
covering all conversion types
- **Real-world testing**: Validates actual CSS processing with edge
cases
## 📊 esbuild Compatibility Analysis
Compared output with esbuild to ensure compatibility:
**Test CSS:**
```css
.test { border-radius: 3.40282e38px; }
.colors { color: rgb(300, -50, 1000); }
.boundaries { width: 2147483648px; }
```
**Key Differences:**
1. **Scientific notation format:**
- esbuild: `3.40282e38` (no explicit + sign)
- Bun: `3.40282e+38` (explicit + sign)
- ✅ Both are mathematically equivalent and valid CSS
2. **Optimization strategy:**
- esbuild: Preserves original literal values
- Bun: Normalizes extremely large values + consolidates selectors
- ✅ Bun's more aggressive optimization results in smaller output
### ❓ Question for Review
**@zackradisic** - Is it acceptable for Bun to diverge from esbuild in
this optimization behavior?
- **Pro**: More aggressive optimization (smaller output, consistent
formatting)
- **Con**: Different output format than esbuild
- **Impact**: Both outputs are functionally identical in browsers
Should we:
1. ✅ Keep current behavior (more aggressive optimization)
2. 🔄 Match esbuild exactly (preserve literal notation)
3. 🎛️ Add flag to control this behavior
## ✅ Testing & Validation
- [x] **Original crash case**: Fixed - no more panics with large
floating-point values
- [x] **All conversion types**: Tested i32, u32, u8, i64 conversions
with edge cases
- [x] **Browser compatibility**: Verified targets parsing works with
extreme values
- [x] **Color processing**: Confirmed RGB/RGBA values properly clamped
to 0-255 range
- [x] **Performance**: No regression - conversions are equally fast
- [x] **Real-world**: TailwindCSS projects with `.rounded-full` work
without crashes
- [x] **Inline snapshots**: Capture exact expected output for future
regression detection
## 🎯 Impact
### Before (Broken)
```bash
$ bun build styles.css
============================================================
panic: integer part of floating point value out of bounds
```
### After (Working)
```bash
$ bun build styles.css
Bundled 1 module in 93ms
styles.css 121 bytes (asset)
```
- ✅ **Fixes crashes** when using TailwindCSS `.rounded-full` class on
Windows
- ✅ **Maintains backward compatibility** for existing projects
- ✅ **Improves robustness** across all CSS float→int conversions
- ✅ **Better optimization** with consistent value normalization
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Various types have a `deepClone` method, but there are two different
signatures in use. Some types, like those in the `css` directory, have
an infallible `deepClone` method that cannot return an error. Others,
like those in `ast`, are fallible and can return `error.OutOfMemory`.
Historically, `BabyList.deepClone` has only worked with the fallible
kind of `deepClone`, necessitating the addition of
`BabyList.deepClone2`, which only works with the *in*fallible kind.
This PR:
* Updates `BabyList.deepClone` so that it works with both kinds of
method
* Updates `BabyList.deepClone2` so that it works with both kinds of
method
* Renames `BabyList.deepClone2` to `BabyList.deepCloneInfallible`
* Adds `bun.handleOom(...)`, which is like `... catch bun.outOfMemory()`
but it can't accidentally catch non-OOM-related errors
* Replaces an occurrence of `anyerror` with a more specific error set
(For internal tracking: fixes STAB-969, STAB-970)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
- Fixes `$.braces(...)` not working properly on non-ascii inputs
- Switches braces code to use `SmallList` to support more deeply nested
brace expansion
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>