formatter-development

安装量: 54
排名: #13777

安装

npx skills add https://github.com/biomejs/biome --skill formatter-development

Purpose Use this skill when implementing or modifying Biome's formatters. It covers the trait-based formatting system, IR generation, comment handling, and testing with Prettier comparison. Prerequisites Install required tools: just install-tools (includes wasm-bindgen-cli and wasm-opt ) Language-specific crates must exist: biome_{lang}syntax , biome_formatter For Prettier comparison: Install bun and run pnpm install in repo root Common Workflows Generate Formatter Boilerplate For a new language (e.g., HTML): just gen-formatter html This generates FormatNodeRule implementations for all syntax nodes. Initial implementations use format_verbatim_node (formats code as-is). Implement FormatNodeRule for a Node Example: Formatting JsIfStatement : use crate :: prelude :: * ; use biome_formatter :: write ; use biome_js_syntax :: { JsIfStatement , JsIfStatementFields } ;

[derive(Debug, Clone, Default)]

pub ( crate ) struct FormatJsIfStatement ; impl FormatNodeRule < JsIfStatement

for FormatJsIfStatement { fn fmt_fields ( & self , node : & JsIfStatement , f : & mut JsFormatter ) -> FormatResult < ( )

{ let JsIfStatementFields { if_token , l_paren_token , test , r_paren_token , consequent , else_clause , } = node . as_fields ( ) ; write! ( f , [ if_token . format ( ) , space ( ) , l_paren_token . format ( ) , test . format ( ) , r_paren_token . format ( ) , space ( ) , consequent . format ( ) , ] ) ? ; if let Some ( else_clause ) = else_clause { write! ( f , [ space ( ) , else_clause . format ( ) ] ) ? ; } Ok ( ( ) ) } } Using IR Primitives Common formatting building blocks: use biome_formatter :: { format_args , write } ; write! ( f , [ token ( "if" ) , // Static text space ( ) , // Single space soft_line_break ( ) , // Break if line is too long hard_line_break ( ) , // Always break // Grouping and indentation group ( & format_args! [ token ( "(" ) , soft_block_indent ( & format_args! [ node . test . format ( ) , ] ) , token ( ")" ) , ] ) , // Conditional formatting format_with ( | f | { if condition { write! ( f , [ token ( "something" ) ] ) } else { write! ( f , [ token ( "other" ) ] ) } } ) , ] ) ? ; Handle Comments use biome_formatter :: format_args ; use biome_formatter :: prelude :: * ; impl FormatNodeRule < JsObjectExpression

for FormatJsObjectExpression { fn fmt_fields ( & self , node : & JsObjectExpression , f : & mut JsFormatter ) -> FormatResult < ( )

{ let JsObjectExpressionFields { l_curly_token , members , r_curly_token , } = node . as_fields ( ) ; write! ( f , [ l_curly_token . format ( ) , block_indent ( & format_args! [ members . format ( ) , // Handle dangling comments (comments not attached to any node) format_dangling_comments ( node . syntax ( ) ) . with_soft_block_indent ( ) ] ) , r_curly_token . format ( ) , ] ) } } Leading and trailing comments are handled automatically by the formatter infrastructure. Compare Against Prettier After implementing formatting, validate against Prettier:

Compare a code snippet

bun packages/prettier-compare/bin/prettier-compare.js --rebuild 'const x={a:1,b:2}'

Compare with explicit language

bun packages/prettier-compare/bin/prettier-compare.js --rebuild -l ts 'const x: number = 1'

Compare a file

bun packages/prettier-compare/bin/prettier-compare.js --rebuild -f path/to/file.tsx

From stdin (useful for editor selections)

echo 'const x = 1' | bun packages/prettier-compare/bin/prettier-compare.js --rebuild -l js Always use --rebuild to ensure WASM bundle matches your Rust changes. Create Snapshot Tests Create test files in tests/specs/ organized by feature: crates/biome_js_formatter/tests/specs/js/ ├── statement/ │ ├── if_statement/ │ │ ├── basic.js │ │ ├── nested.js │ │ └── with_comments.js │ └── for_statement/ │ └── various.js Example test file basic.js : if ( condition ) { doSomething ( ) ; } if ( condition ) doSomething ( ) ; if ( condition ) { doSomething ( ) ; } else { doOther ( ) ; } Run tests: cd crates/biome_js_formatter cargo test Review snapshots: cargo insta review Test with Custom Options Create options.json in the test folder: { "formatter" : { "indentStyle" : "space" , "indentWidth" : 2 , "lineWidth" : 80 } , "javascript" : { "formatter" : { "quoteStyle" : "single" , "semicolons" : "asNeeded" } } } This applies to all test files in that folder. Format and Build After changes: just f

Format Rust code

just l

Lint

just gen-formatter

Regenerate formatter infrastructure if needed

Tips
format_verbatim_node
Initial generated code uses this - replace it with proper IR as you implement formatting
Space tokens
Use
space()
instead of
token(" ")
for semantic spacing
Breaking
Use
soft_line_break()
for optional breaks,
hard_line_break()
for mandatory breaks
Grouping
Wrap related elements in
group()
to keep them together when possible
Indentation
Use
block_indent()
for block-level indentation,
indent()
for inline
Lists
Use
join_nodes_with_soft_line()
or
join_nodes_with_hardline()
for formatting lists
Mandatory tokens
Use
node.token().format()
for tokens that exist in AST, not
token("(")
Debugging
Use
dbg_write!
macro (like
dbg!
) to see IR elements:
dbg_write!(f, [token("hello")])?;
Don't fix code
Formatter should format existing code, not attempt to fix syntax errors IR Primitives Reference // Whitespace space ( ) // Single space soft_line_break ( ) // Break if needed hard_line_break ( ) // Always break soft_line_break_or_space ( ) // Space or break // Indentation indent ( & content ) // Indent content block_indent ( & content ) // Block-level indent soft_block_indent ( & content ) // Indent with soft breaks // Grouping group ( & content ) // Keep together if possible conditional_group ( & content ) // Advanced grouping // Text token ( "text" ) // Static text dynamic_token ( & text , pos ) // Dynamic text with position // Utility format_with ( | f | { ... } ) // Custom formatting function format_args! [ a , b , c ] // Combine multiple items if_group_breaks ( & content ) // Only if group breaks if_group_fits_on_line ( & content ) // Only if fits References Full guide: crates/biome_formatter/CONTRIBUTING.md JS-specific: crates/biome_js_formatter/CONTRIBUTING.md Prettier comparison tool: packages/prettier-compare/ Examples: crates/biome_js_formatter/src/js/ for real implementations
返回排行榜