Skip to content

fix(parser/css): handle nested rules in embedded snippets#9995

Merged
siketyan merged 2 commits intobiomejs:mainfrom
siketyan:fix-issue-9994-styled-css
Apr 15, 2026
Merged

fix(parser/css): handle nested rules in embedded snippets#9995
siketyan merged 2 commits intobiomejs:mainfrom
siketyan:fix-issue-9994-styled-css

Conversation

@siketyan
Copy link
Copy Markdown
Member

@siketyan siketyan commented Apr 15, 2026

Summary

Note

AI Assistance Disclosure: I used the Codex agent to investigate the problem, fix the issue, and create regression tests. All changes and output are reviewed by a human (me).

Fixes #9994

CSS parser now correctly parses top-level rules inside embedded snippets when a declaration follows it. For example:

const MyComponent = styled.div`
  div:first-of-type {
    color: white;
  }
  background-color: black;
`

Test Plan

Added a parser test and an integration test.

Docs

N/A

@siketyan siketyan requested review from a team April 15, 2026 12:28
@siketyan siketyan self-assigned this Apr 15, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 15, 2026

🦋 Changeset detected

Latest commit: 4281ffc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added A-Project Area: project A-Parser Area: parser L-CSS Language: CSS and super languages labels Apr 15, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 15, 2026

Merging this PR will not alter performance

✅ 29 untouched benchmarks
⏩ 227 skipped benchmarks1


Comparing siketyan:fix-issue-9994-styled-css (4281ffc) with main (15276a5)2

Open in CodSpeed

Footnotes

  1. 227 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (15276a5) during the generation of this report, so 15276a5 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@siketyan siketyan marked this pull request as ready for review April 15, 2026 12:40
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: caf2b2eb-46fe-4bde-b94a-e36e26cf4508

📥 Commits

Reviewing files that changed from the base of the PR and between 15276a5 and 4281ffc.

📒 Files selected for processing (4)
  • .changeset/fix-issue-9994-styled-css.md
  • crates/biome_css_parser/src/lib.rs
  • crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs
  • crates/biome_service/src/workspace/server.tests.rs

Walkthrough

This PR fixes parsing of nested CSS rules within styled-embedded snippets when declarations follow the nested rules. The fix introduces detection of top-level qualified rules in styled embedding contexts, refines speculative declaration parsing validation to prevent incorrect parse handling at block boundaries, and extends parse control flow to attempt qualified-rule parsing when declaration parsing fails in styled top-level contexts. A changeset, parser test, and integration test validate the fix.

Possibly related PRs

Suggested reviewers

  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: fixing CSS parser handling of nested rules in embedded snippets.
Description check ✅ Passed The description clearly relates to the changeset, explaining the fix, providing a concrete example, and noting tests added.
Linked Issues check ✅ Passed The changes fully address issue #9994 by implementing parser fixes to handle nested CSS rules followed by declarations in embedded snippets, with regression tests added.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the CSS parser for nested rules in embedded snippets; no unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@siketyan siketyan merged commit 4da9caf into biomejs:main Apr 15, 2026
29 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 15, 2026
OIRNOIR pushed a commit to OIRNOIR/YouTube-Helper-Server that referenced this pull request Apr 24, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [@biomejs/biome](https://biomejs.dev) ([source](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome)) | patch | `2.4.12` → `2.4.13` |

---

### Release Notes

<details>
<summary>biomejs/biome (@&#8203;biomejs/biome)</summary>

### [`v2.4.13`](https://github.com/biomejs/biome/blob/HEAD/packages/@&#8203;biomejs/biome/CHANGELOG.md#2413)

[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/[email protected]...@biomejs/[email protected])

##### Patch Changes

- [#&#8203;9969](biomejs/biome#9969) [`c5eb92b`](biomejs/biome@c5eb92b) Thanks [@&#8203;officialasishkumar](https://github.com/officialasishkumar)! - Added the nursery rule [`noUnnecessaryTemplateExpression`](https://biomejs.dev/linter/rules/no-unnecessary-template-expression/), which disallows template literals that only contain string literal expressions. These can be replaced with a simpler string literal.

  For example, the following code triggers the rule:

  ```js
  const a = `${"hello"}`; // can be 'hello'
  const b = `${"prefix"}_suffix`; // can be 'prefix_suffix'
  const c = `${"a"}${"b"}`; // can be 'ab'
  ```

- [#&#8203;10037](biomejs/biome#10037) [`f785e8c`](biomejs/biome@f785e8c) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - Fixed [#&#8203;9810](biomejs/biome#9810): [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/) no longer reports false positives on a getter with a matching setter in the same namespace.

  ```ts
  class Store {
    get status(): string {
      if (Math.random() > 0.5) return "loading";
      return "idle";
    }
    set status(v: string) {}
  }
  ```

- [#&#8203;10084](biomejs/biome#10084) [`5e2f90c`](biomejs/biome@5e2f90c) Thanks [@&#8203;jiwon79](https://github.com/jiwon79)! - Fixed [#&#8203;10034](biomejs/biome#10034): [`noUselessEscapeInRegex`](https://biomejs.dev/linter/rules/no-useless-escape-in-regex/) no longer flags escapes of `ClassSetReservedPunctuator` characters (`&`, `!`, `#`, `%`, `,`, `:`, `;`, `<`, `=`, `>`, `@`, `` ` ``, `~`) inside `v`-flag character classes as useless. These characters are reserved as individual code points in `v`-mode, so the escape is required.

  The following pattern is now considered valid:

  ```js
  /[a-z\&]/v;
  ```

- [#&#8203;10063](biomejs/biome#10063) [`c9ffa16`](biomejs/biome@c9ffa16) Thanks [@&#8203;Netail](https://github.com/Netail)! - Added extra rule sources from ESLint CSS. `biome migrate eslint` should do a bit better detecting rules in your eslint configurations.

- [#&#8203;10035](biomejs/biome#10035) [`946b50e`](biomejs/biome@946b50e) Thanks [@&#8203;Netail](https://github.com/Netail)! - Fixed [#&#8203;10032](biomejs/biome#10032): [useIframeSandbox](https://biomejs.dev/linter/rules/use-iframe-sandbox/) now flags if there's no initializer value.

- [#&#8203;9865](biomejs/biome#9865) [`68fb8d4`](biomejs/biome@68fb8d4) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`useDomNodeTextContent`](https://biomejs.dev/linter/rules/use-dom-node-text-content/), which prefers `textContent` over `innerText` for DOM node text access and destructuring.

  For example, the following snippet triggers the rule:

  ```js
  const foo = node.innerText;
  ```

- [#&#8203;10023](biomejs/biome#10023) [`bd1e74f`](biomejs/biome@bd1e74f) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Added a new nursery rule [`noReactNativeDeepImports`](https://biomejs.dev/linter/rules/no-react-native-deep-imports/) that disallows deep imports from the `react-native` package. Internal paths like `react-native/Libraries/...` are not part of the public API and may change between versions.

  For example, the following code triggers the rule:

  ```js
  import View from "react-native/Libraries/Components/View/View";
  ```

- [#&#8203;9885](biomejs/biome#9885) [`3dce737`](biomejs/biome@3dce737) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added a new nursery rule [`useDomQuerySelector`](https://biomejs.dev/linter/rules/use-dom-query-selector/) that prefers `querySelector()` and `querySelectorAll()` over older DOM query methods such as `getElementById()` and `getElementsByClassName()`.

- [#&#8203;9995](biomejs/biome#9995) [`4da9caf`](biomejs/biome@4da9caf) Thanks [@&#8203;siketyan](https://github.com/siketyan)! - Fixed [#&#8203;9994](biomejs/biome#9994): Biome now parses nested CSS rules correctly when declarations follow them inside embedded snippets.

- [#&#8203;10009](biomejs/biome#10009) [`b41cc5a`](biomejs/biome@b41cc5a) Thanks [@&#8203;Jayllyz](https://github.com/Jayllyz)! - Fixed [#&#8203;10004](biomejs/biome#10004): [`noComponentHookFactories`](https://biomejs.dev/linter/rules/no-component-hook-factories/) no longer reports false positives for object methods and class methods.

- [#&#8203;9988](biomejs/biome#9988) [`eabf54a`](biomejs/biome@eabf54a) Thanks [@&#8203;Netail](https://github.com/Netail)! - Tweaked the diagnostics range for [useAltText](https://biomejs.dev/linter/rules/use-alt-text), [useButtonType](https://biomejs.dev/linter/rules/use-button-type), [useHtmlLang](https://biomejs.dev/linter/rules/use-html-lang), [useIframeTitle](https://biomejs.dev/linter/rules/use-iframe-title), [useValidAriaRole](https://biomejs.dev/linter/rules/use-valid-aria-role) & [useIfameSandbox](https://biomejs.dev/linter/rules/use-iframe-sandbox) to report on the opening tag instead of the full tag.

- [#&#8203;10043](biomejs/biome#10043) [`fc65902`](biomejs/biome@fc65902) Thanks [@&#8203;mujpao](https://github.com/mujpao)! - Fixed [#&#8203;10003](biomejs/biome#10003): Biome no longer panics when parsing Svelte files containing `{#}`.

- [#&#8203;9815](biomejs/biome#9815) [`5cc83b1`](biomejs/biome@5cc83b1) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`noLoopFunc`](https://biomejs.dev/linter/rules/no-loop-func/). When enabled, it warns when a function declared inside a loop captures outer variables that can change across iterations.

- [#&#8203;9702](biomejs/biome#9702) [`ef470ba`](biomejs/biome@ef470ba) Thanks [@&#8203;ryan-m-walker](https://github.com/ryan-m-walker)! - Added the nursery rule [`useRegexpTest`](https://biomejs.dev/linter/rules/use-regexp-test/) that enforces `RegExp.prototype.test()` over `String.prototype.match()` and `RegExp.prototype.exec()` in boolean contexts. `test()` returns a boolean directly, avoiding unnecessary computation of match results.

  **Invalid**

  ```js
  if ("hello world".match(/hello/)) {
  }
  ```

  **Valid**

  ```js
  if (/hello/.test("hello world")) {
  }
  ```

- [#&#8203;9743](biomejs/biome#9743) [`245307d`](biomejs/biome@245307d) Thanks [@&#8203;leetdavid](https://github.com/leetdavid)! - Fixed [#&#8203;2245](biomejs/biome#2245): Svelte `<script>` tag language detection when the `generics` attribute contains `>` characters (e.g., `<script lang="ts" generics="T extends Record<string, unknown>">`). Biome now correctly recognizes TypeScript in such script blocks.

- [#&#8203;10046](biomejs/biome#10046) [`0707de7`](biomejs/biome@0707de7) Thanks [@&#8203;Conaclos](https://github.com/Conaclos)! - Fixed [#&#8203;10038](biomejs/biome#10038): [`organizeImports`](https://biomejs.dev/assist/actions/organize-imports/) now sorts imports in TypeScript modules and declaration files.

  ```diff
    declare module "mymodule" {
  -  	import type { B } from "b";
    	import type { A } from "a";
  +  	import type { B } from "b";
    }
  ```

- [#&#8203;10012](biomejs/biome#10012) [`94ccca9`](biomejs/biome@94ccca9) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Added the nursery rule [`noReactNativeLiteralColors`](https://biomejs.dev/linter/rules/no-react-native-literal-colors/), which disallows color literals inside React Native styles.

  The rule belongs to the `reactNative` domain. It reports properties whose name contains `color` and whose value is a string literal when they appear inside a `StyleSheet.create(...)` call or inside a JSX attribute whose name contains `style`.

  ```jsx
  // Invalid
  const Hello = () => <Text style={{ backgroundColor: "#FFFFFF" }}>hi</Text>;

  const styles = StyleSheet.create({
    text: { color: "red" },
  });
  ```

  ```jsx
  // Valid
  const red = "#f00";
  const styles = StyleSheet.create({
    text: { color: red },
  });
  ```

- [#&#8203;10005](biomejs/biome#10005) [`131019e`](biomejs/biome@131019e) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Added the nursery rule [`noReactNativeRawText`](https://biomejs.dev/linter/rules/no-react-native-raw-text/), which disallows raw text outside of `<Text>` components in React Native.

  The rule belongs to the new `reactNative` domain.

  ```jsx
  // Invalid
  <View>some text</View>
  <View>{'some text'}</View>
  ```

  ```jsx
  // Valid
  <View>
    <Text>some text</Text>
  </View>
  ```

  Additional components can be allowlisted through the `skip` option:

  ```json
  {
    "options": {
      "skip": ["Title"]
    }
  }
  ```

- [#&#8203;9911](biomejs/biome#9911) [`1603f78`](biomejs/biome@1603f78) Thanks [@&#8203;Netail](https://github.com/Netail)! - Added the nursery rule [`noJsxLeakedDollar`](https://biomejs.dev/linter/rules/no-jsx-leaked-dollar), which flags text nodes with a trailing `$` if the next sibling node is a JSX expression. This could be an unintentional mistake, resulting in a '$' being rendered as text in the output.

  **Invalid**:

  ```jsx
  function MyComponent({ user }) {
    return <div>Hello ${user.name}</div>;
  }
  ```

- [#&#8203;9999](biomejs/biome#9999) [`f42405f`](biomejs/biome@f42405f) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - Fixed `noMisleadingReturnType` incorrectly flagging functions with reassigned `let` variables.

- [#&#8203;10075](biomejs/biome#10075) [`295f97f`](biomejs/biome@295f97f) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixed [`#9983`](biomejs/biome#9983): Biome now parses functions declared inside Svelte `#snippet` blocks without throwing errors.

- [#&#8203;10006](biomejs/biome#10006) [`cf4c1c9`](biomejs/biome@cf4c1c9) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - Fixed [#&#8203;9810](biomejs/biome#9810): `noMisleadingReturnType` incorrectly flagging nested object literals with widened properties.

- [#&#8203;10033](biomejs/biome#10033) [`11ddc05`](biomejs/biome@11ddc05) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Added the nursery rule [`useReactNativePlatformComponents`](https://biomejs.dev/linter/rules/use-react-native-platform-components/) that ensures platform-specific React Native components (e.g. `ProgressBarAndroid`, `ActivityIndicatorIOS`) are only imported in files with a matching platform suffix. It also reports when Android and iOS components are mixed in the same file.

  The following code triggers the rule when the file does not have an `.android.js` suffix:

  ```js
  // file.js
  import { ProgressBarAndroid } from "react-native";
  ```

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzkuNiIsInVwZGF0ZWRJblZlciI6IjQzLjEzOS42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://git.oirnoir.dev/OIRNOIR/YouTube-Helper-Server/pulls/1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser A-Project Area: project L-CSS Language: CSS and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Cannot parse nested CSS block inside embedded snippets when a property follows

2 participants