diff --git a/CHANGELOG.md b/CHANGELOG.md index 645a8873bc3..721f2f9b5f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ This fixes a regression in version 0.18.4 where combining `--minify-syntax` with `--keep-names` could cause expressions with side effects after a function declaration to be considered side-effect free for tree shaking purposes. The reason was because `--keep-names` generates an expression statement containing a call to a helper function after the function declaration with a special flag that makes the function call able to be tree shaken, and then `--minify-syntax` could potentially merge that expression statement with following expressions without clearing the flag. This release fixes the bug by clearing the flag when merging expression statements together. +* Fix an incorrect warning about CSS nesting ([#3197](https://github.com/evanw/esbuild/issues/3197)) + + A warning is currently generated when transforming nested CSS to a browser that doesn't support `:is()` because transformed nested CSS may need to use that feature to represent nesting. This was previously always triggered when an at-rule was encountered in a declaration context. Typically the only case you would encounter this is when using CSS nesting within a selector rule. However, there is a case where that's not true: when using a margin at-rule such as `@top-left` within `@page`. This release avoids incorrectly generating a warning in this case by checking that the at-rule is within a selector rule before generating a warning. + ## 0.18.9 * Fix `await using` declarations inside `async` generator functions diff --git a/internal/bundler_tests/bundler_css_test.go b/internal/bundler_tests/bundler_css_test.go index 65597e0e1a8..49a439bbb30 100644 --- a/internal/bundler_tests/bundler_css_test.go +++ b/internal/bundler_tests/bundler_css_test.go @@ -733,6 +733,9 @@ func TestCSSNestingOldBrowser(t *testing.T) { "/media-hash.css": `@media screen { #id { color: red; } }`, "/media-plus.css": `@media screen { + b { color: red; } }`, "/media-tilde.css": `@media screen { ~ b { color: red; } }`, + + // See: https://github.com/evanw/esbuild/issues/3197 + "/page-no-warning.css": `@page { @top-left { background: red } }`, }, entryPaths: []string{ "/nested-@layer.css", @@ -768,6 +771,8 @@ func TestCSSNestingOldBrowser(t *testing.T) { "/media-hash.css", "/media-plus.css", "/media-tilde.css", + + "/page-no-warning.css", }, options: config.Options{ Mode: config.ModeBundle, diff --git a/internal/bundler_tests/snapshots/snapshots_css.txt b/internal/bundler_tests/snapshots/snapshots_css.txt index d45980bc47c..a75c0a1b261 100644 --- a/internal/bundler_tests/snapshots/snapshots_css.txt +++ b/internal/bundler_tests/snapshots/snapshots_css.txt @@ -398,6 +398,14 @@ a, } } +---------- /out/page-no-warning.css ---------- +/* page-no-warning.css */ +@page { + @top-left { + background: red; + } +} + ================================================================================ TestDataURLImportURLInCSS ---------- /out/entry.css ---------- diff --git a/internal/css_parser/css_parser.go b/internal/css_parser/css_parser.go index 3f211571be2..49fa64b4181 100644 --- a/internal/css_parser/css_parser.go +++ b/internal/css_parser/css_parser.go @@ -26,6 +26,7 @@ type parser struct { index int end int legalCommentIndex int + inSelectorSubtree int prevError logger.Loc options Options shouldLowerNesting bool @@ -405,7 +406,9 @@ func (p *parser) parseListOfDeclarations(opts listOfDeclarationsOpts) (list []cs return case css_lexer.TAtKeyword: - p.reportUseOfNesting(p.current().Range, false) + if p.inSelectorSubtree > 0 { + p.reportUseOfNesting(p.current().Range, false) + } list = append(list, p.parseAtRule(atRuleContext{ isDeclarationList: true, canInlineNoOpNesting: opts.canInlineNoOpNesting, @@ -1725,9 +1728,11 @@ func (p *parser) parseSelectorRuleFrom(preludeStart int, isTopLevel bool, opts p selector := css_ast.RSelector{Selectors: list} matchingLoc := p.current().Range.Loc if p.expect(css_lexer.TOpenBrace) { + p.inSelectorSubtree++ selector.Rules = p.parseListOfDeclarations(listOfDeclarationsOpts{ canInlineNoOpNesting: canInlineNoOpNesting, }) + p.inSelectorSubtree-- p.expectWithMatchingLoc(css_lexer.TCloseBrace, matchingLoc) return css_ast.Rule{Loc: p.tokens[preludeStart].Range.Loc, Data: &selector} }