--- name: biome-developer description: General development best practices and common gotchas when working on Biome. Use for avoiding common mistakes, understanding Biome-specific patterns, and learning technical tips. Examples:Working with Biome's AST and syntax nodesUnderstanding string extraction methodsHandling embedded languages and directives --- ## Purpose This skill provides general development best practices, common gotchas, and Biome-specific patterns that apply across different areas of the codebase. Use this as a reference when you encounter unfamiliar APIs or need to avoid common mistakes. ## Prerequisites - Basic familiarity with Rust - Understanding of Biome's architecture (parser, analyzer, formatter) - Development environment set up (see CONTRIBUTING.md) ## Common Gotchas and Best Practices ### Working with AST and Syntax Nodes **DO:** - ✅ Use parser crate's `quick_test` to inspect AST structure before implementing - ✅ Understand the node hierarchy and parent-child relationships - ✅ Check both general cases AND specific types (e.g., Vue has both `VueDirective` and `VueV*ShorthandDirective`) - ✅ Verify your solution works for all relevant variant types, not just the first one you find **DON'T:** - ❌ Build the full Biome binary just to inspect syntax (expensive) - use parser crate's `quick_test` instead - ❌ Assume syntax patterns without inspecting the AST first **Example - Inspecting AST:** ```rust // In crates/biome_html_parser/tests/quick_test.rs // Modify the quick_test function: #[test] pub fn quick_test() { let code = r#""#; let source_type = HtmlFileSource::svelte(); let options = HtmlParseOptions::from(&source_type); let root = parse_html(code, options); dbg!(&root.syntax()); // Shows full AST structure } ``` Run: `just qt biome_html_parser` ### String Extraction and Text Handling **DO:** - ✅ Use `inner_string_text()` when extracting content from quoted strings (removes quotes) - ✅ Use `text_trimmed()` when you need the full token text without leading/trailing whitespace - ✅ Use `token_text_trimmed()` on nodes like `HtmlAttributeName` to get the text content - ✅ Verify whether values use `HtmlString` (quotes) or `HtmlTextExpression` (curly braces) **DON'T:** - ❌ Use `text_trimmed()` when you need `inner_string_text()` for extracting quoted string contents **Example - String Extraction:** ```rust // WRONG: text_trimmed() includes quotes let html_string = value.as_html_string()?; let content = html_string.value_token()?.text_trimmed(); // Returns: "\"handler\"" // CORRECT: inner_string_text() removes quotes let html_string = value.as_html_string()?; let inner_text = html_string.inner_string_text().ok()?; let content = inner_text.text(); // Returns: "handler" ``` ### Working with Embedded Languages **DO:** - ✅ Verify changes work for different value formats (quoted strings vs text expressions) when handling multiple frameworks - ✅ Use appropriate `EmbeddingKind` for context (Vue, Svelte, Astro, etc.) - ✅ Check if embedded content needs `is_source: true` (script tags) vs `is_source: false` (template expressions) - ✅ Calculate offsets correctly: token start + 1 for opening quote, or use `text_range().start()` for text expressions **DON'T:** - ❌ Assume all frameworks use the same syntax (Vue uses quotes, Svelte uses curly braces) - ❌ Implement features for "widely used" patterns without evidence - ask the user first **Example - Different Value Formats:** ```rust // Vue directives use quoted strings: @click="handler" let html_string = value.as_html_string()?; let inner_text = html_string.inner_string_text().ok()?; // Svelte directives use text expressions: on:click={handler} let text_expression = value.as_html_attribute_single_text_expression()?; let expression = text_expression.expression().ok()?; ``` ### Borrow Checker and Temporary Values **DO:** - ✅ Use intermediate `let` bindings to avoid temporary value borrows that get dropped - ✅ Store method results that return owned values before calling methods on them **DON'T:** - ❌ Create temporary value borrows that get dropped before use **Example - Avoiding Borrow Issues:** ```rust // WRONG: Temporary borrow gets dropped let html_string = value.value().ok()?.as_html_string()?; let token = html_string.value_token().ok()?; // ERROR: html_string dropped // CORRECT: Store intermediate result let value_node = value.value().ok()?; let html_string = value_node.as_html_string()?; let token = html_string.value_token().ok()?; // OK ``` ### Clippy and Code Style **DO:** - ✅ Use `let` chains to collapse nested `if let` statements (cleaner and follows Rust idioms) - ✅ Run `just l` before committing to catch clippy warnings - ✅ Fix clippy suggestions unless there's a good reason not to **DON'T:** - ❌ Ignore clippy warnings - they often catch real issues or suggest better patterns **Example - Collapsible If:** ```rust // WRONG: Nested if let (clippy::collapsible_if warning) if let Some(directive) = VueDirective::cast_ref(&element) { if let Some(initializer) = directive.initializer() { // ... do something } } // CORRECT: Use let chains if let Some(directive) = VueDirective::cast_ref(&element) && let Some(initializer) = directive.initializer() { // ... do something } ``` ### Legacy and Deprecated Syntax **DO:** - ✅ Ask users before implementing deprecated/legacy syntax support - ✅ Wait for user demand before spending time on legacy features - ✅ Document when features are intentionally not supported due to being legacy **DON'T:** - ❌ Implement legacy/deprecated syntax without checking with the user first - ❌ Claim patterns are "widely used" or "common" without evidence **Example:** Svelte's `on:click` event handler syntax is legacy (Svelte 3/4). Modern Svelte 5 runes mode uses regular attributes. Unless users specifically request it, don't implement legacy syntax support. ### Testing and Development **DO:** - ✅ Use `just qt ` to run quick tests (handles test execution automatically) - ✅ Review snapshot changes carefully - don't blindly accept - ✅ Test with multiple variants when working with enums (e.g., all `VueV*ShorthandDirective` types) - ✅ Add tests for both valid and invalid cases - ✅ Use CLI tests for testing embedded languages (Vue/Svelte directives, etc.) **DON'T:** - ❌ Blindly accept all snapshot changes - ❌ Try to test embedded languages in analyzer packages (they don't have embedding capabilities) ## Pattern Matching Tips ### Working with Node Variants When working with enum variants (like `AnySvelteDirective`), check if there are also non-enum types that need handling: ```rust // Check AnySvelteDirective enum (bind:, class:, style:, etc.) if let Some(directive) = AnySvelteDirective::cast_ref(&element) { // Handle special Svelte directives } // But also check regular HTML attributes with specific prefixes if let Some(attribute) = HtmlAttribute::cast_ref(&element) { if let Ok(name) = attribute.name() { // Some directives might be parsed as regular attributes } } ``` ### Checking Multiple Variant Types For frameworks with multiple directive syntaxes, handle each type: ```rust // Vue has multiple shorthand types if let Some(directive) = VueVOnShorthandDirective::cast_ref(&element) { // Handle @click } if let Some(directive) = VueVBindShorthandDirective::cast_ref(&element) { // Handle :prop } if let Some(directive) = VueVSlotShorthandDirective::cast_ref(&element) { // Handle #slot } if let Some(directive) = VueDirective::cast_ref(&element) { // Handle v-if, v-show, etc. } ``` ## Common API Confusion ### String/Text Methods | Method | Use When | Returns | | --- | --- | --- | | `inner_string_text()` | Extracting content from quoted strings | Content without quotes | | `text_trimmed()` | Getting token text without whitespace | Full token text | | `token_text_trimmed()` | Getting text from nodes like `HtmlAttributeName` | Node text content | | `text()` | Getting raw text | Exact text as written | ### Value Extraction Methods | Type | Method | Framework | | --- | --- | --- | | `HtmlString` | `inner_string_text()` | Vue (quotes) | | `HtmlAttributeSingleTextExpression` | `expression()` | Svelte (curly braces) | | `HtmlTextExpression` | `html_literal_token()` | Template expressions | ## References - Main contributing guide: `../../CONTRIBUTING.md` - Testing workflows: `../testing-codegen/SKILL.md` - Parser development: `../parser-development/SKILL.md` - Biome internals docs: https://biomejs.dev/internals ## Documentation and Markdown Formatting **DO:** - ✅ Use spaces around table separators: `| --- | --- | --- |` (not `|---|---|---|`) - ✅ Ensure all Markdown tables follow "compact" style with proper spacing - ✅ Test documentation changes with markdown linters before committing **DON'T:** - ❌ Use compact table separators without spaces (causes CI linting failures) **Example - Table Formatting:** ```markdown | Method | Use When | Returns | |--------|----------|---------| | Method | Use When | Returns | | --- | --- | --- | ``` The CI uses `markdownlint-cli2` which enforces the "compact" style requiring spaces. ## When to Use This Skill Load this skill when: - Working with unfamiliar Biome APIs - Getting borrow checker errors with temporary values - Extracting strings or text from syntax nodes - Implementing support for embedded languages (Vue, Svelte, etc.) - Wondering why your AST inspection doesn't match expectations - Making decisions about legacy/deprecated syntax support - Writing or updating markdown documentation