diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c12ff60139..7f45e767377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## Unreleased + +* Fix CSS nesting transform for triple-nested rules that start with a combinator ([#3046](https://github.com/evanw/esbuild/issues/3046)) + + This release fixes a bug with esbuild where triple-nested CSS rules that start with a combinator were not transformed correctly for older browsers. Here's an example of such a case before and after this bug fix: + + ```css + /* Original input */ + .a { + color: red; + > .b { + color: green; + > .c { + color: blue; + } + } + } + + /* Old output (with --target=chrome90) */ + .a { + color: red; + } + .a > .b { + color: green; + } + .a .b > .c { + color: blue; + } + + /* New output (with --target=chrome90) */ + .a { + color: red; + } + .a > .b { + color: green; + } + .a > .b > .c { + color: blue; + } + ``` + ## 0.17.15 * Allow keywords as type parameter names in mapped types ([#3033](https://github.com/evanw/esbuild/issues/3033)) diff --git a/internal/css_parser/css_nesting.go b/internal/css_parser/css_nesting.go index db9e0f266c0..549c4367fa2 100644 --- a/internal/css_parser/css_nesting.go +++ b/internal/css_parser/css_nesting.go @@ -212,6 +212,9 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac var subclassSelectorPrefix []css_ast.SS + // Copy over the combinator, if any + sel.Combinator = single.Combinator + // Insert the type selector if single.TypeSelector != nil { if sel.TypeSelector != nil { diff --git a/internal/css_parser/css_parser_test.go b/internal/css_parser/css_parser_test.go index 5a61ef7a008..ae33d625eb3 100644 --- a/internal/css_parser/css_parser_test.go +++ b/internal/css_parser/css_parser_test.go @@ -883,6 +883,8 @@ func TestNestedSelector(t *testing.T) { expectPrintedLower(t, ".foo { & .bar { color: red } color: blue }", ".foo {\n color: blue;\n}\n.foo .bar {\n color: red;\n}\n") expectPrintedLower(t, ".foo { color: blue; & .bar { color: red } zoom: 2 }", ".foo {\n color: blue;\n zoom: 2;\n}\n.foo .bar {\n color: red;\n}\n") expectPrintedLower(t, ".a, .b { .c, .d { color: red } }", ":is(.a, .b) :is(.c, .d) {\n color: red;\n}\n") + expectPrintedLower(t, ".a { color: red; > .b { color: green; > .c { color: blue } } }", ".a {\n color: red;\n}\n.a > .b {\n color: green;\n}\n.a > .b > .c {\n color: blue;\n}\n") + expectPrintedLower(t, "> .a { color: red; > .b { color: green; > .c { color: blue } } }", "> .a {\n color: red;\n}\n> .a > .b {\n color: green;\n}\n> .a > .b > .c {\n color: blue;\n}\n") expectPrintedLower(t, ".foo, .bar, .foo:before, .bar:after { &:hover { color: red } }", ":is(.foo, .bar):hover {\n color: red;\n}\n") expectPrintedLower(t, ".foo, .bar:before { &:hover { color: red } }", ".foo:hover {\n color: red;\n}\n") expectPrintedLower(t, ".foo, .bar:before { :hover & { color: red } }", ":hover .foo {\n color: red;\n}\n")