Skip to content

JavaScript

Files ending in .js, .mjs, .cjs and .jsx are formatted by the JavaScript pack (rainbow_fmt.languages.javascript, grammar tree-sitter-javascript). The defaults follow Prettier's rules, with the core's 4-space indentation and without semicolons; byte-identical Prettier output is not a goal, and where users differ there is an option.

const total=items.filter(item=>item.active).map(item=>item.price).reduce((a,b)=>a+b,0);
if(total>limit){notify({user,total,message:"over the limit"})}

becomes

const total = items
    .filter(item => item.active)
    .map(item => item.price)
    .reduce((a, b) => a + b, 0)
if (total > limit) {
    notify({ user, total, message: "over the limit" })
}

Prettier's own style is indent_size = 2:

[language.javascript]
semicolons = "always"   # Prettier's default

[core]
indent_size = 2

([core] applies to all languages; use an [[override]] for *.js files to change it for JavaScript only, see configuration.md.)

What changes and what does not

The pack changes layout only. Strings, template literals, regular expressions, numbers and comment texts are printed as written, and parentheses are never added or removed. What it may add or remove are statement-final semicolons, empty statements, semicolons between class members and trailing commas; the verifier checks that nothing else changed.

  • Indentation comes from the tree (core.indent_style, core.indent_size); a non-empty block is always broken, } else {, } catch (e) { and } while (x) join the closing brace.
  • Spacing within a line: one space around binary and assignment operators, =>, ? and :, after commas, keywords and : of object members; none inside brackets and parentheses, around . and ?., before the ( of calls and parameters (function () {} and async () => excepted), after unary operators (typeof, void, delete excepted) and .... Braces of objects, patterns and import/export lists get spaces inside (bracket_spacing).
  • Brackets are printed flat if they fit, otherwise one item per line with a trailing comma (shared.trailing_comma, whose default is "multiline" for JavaScript; never after a rest element). An object written with a line break after { stays broken (object_wrap); an array of two or more arrays or objects (a matrix) is always broken; an array of numbers is filled.
  • Call arguments: a function, arrow function or object as the last argument hugs the parentheses (it("works", () => { … })), as does a function as the first of two arguments (setTimeout(() => { … }, 1000)). A lone object pattern parameter hugs its parentheses too.
  • Expressions break when too long: after the operators of a binary expression at its lowest precedence level (indented inside arguments), after = for a binary or string value, before ? and : of a conditional, after => of an arrow function whose body is not a block or object, and before each . of a member chain with two or more .call()s after its head. A chain also breaks when a call other than the last spans lines (a function argument).
  • Declarations with several declarators and any initializer put one declarator per line (let a = 1, / b).
  • Blank lines are kept, at most core.max_blank_lines; none at the start or end of a block or the file.
  • Comments: a comment after code stays on its line, one space after it; a comment on its own line is indented to its block; a comment inside brackets stays with its item and forces the brackets to break. A block comment whose lines all start with * (JSDoc) is re-indented.
  • Decorators of a class go on their own lines; those of a class member stay where they were written (same line or own line).
  • Inline directives: // rainbow: off, // rainbow: skip-next, // rainbow: set … (also as /* … */), Prettier's // prettier-ignore (configuration.md).
  • JSX elements are printed exactly as written (their lines are not re-indented).

Semicolons

language.javascript.semicolons:

  • "as_needed" (default): statement-final semicolons and empty statements are removed. A statement that would continue the previous line gets a leading ;: one starting with (, [, `, +, -, / (a regular expression) or < (JSX):
let a = 1
;[b, c] = [c, b]

So does a statement starting with a name like in1, in_x or in$, which the grammar would read as the operator in after a line break.

A class field keeps its ; where the next member would join it: before a member named in or instanceof, a computed field or a computed or generator method, and after a field named static, get or set without a value (Prettier's rules). - "always": every statement that may end with ; ends with one, and so does every class field. - "preserve": semicolons as written. A ; at the start of a line after a statement is that statement's semicolon, so it moves to the end of the previous line.

The body of if (x);, for (;;); and while (x); is an empty statement and always kept.

Options

Key Values Default
language.javascript.semicolons "as_needed", "always", "preserve" (see above) "as_needed"
language.javascript.object_wrap "preserve": an object written with a line break between { and its first member stays broken; "fit": objects break only when too long "preserve"
language.javascript.bracket_spacing true: { a }; false: {a} (objects, patterns, import/export lists) true
language.javascript.return_semicolons "as_statements": as semicolons; "unless_last": with semicolons = "as_needed", a return followed by another statement in its block (or an if (…) return x followed by one) ends with ;, the last return of a block does not "as_statements"
language.javascript.bracket_hug true: an array whose items are all non-empty objects hugs them ([{ … }, { … }]) when it does not fit on one line false
language.javascript.binary_operator_break "after" or "before" the operators of a broken expression "after"
shared.trailing_comma "never", "multiline" "multiline" for JavaScript

Differences from Prettier

  • Parentheses are never added: a && b || c, a + b % c and new Foo stay as written, arrow parameters stay x => or (x) =>, and a long return or ternary is broken without wrapping it in parentheses. Nor are they removed ((a)() gets a ; guard where Prettier prints a()).
  • Quotes and numbers are printed as written ('a', 0XFF, .5).
  • JSX is printed as written, not re-laid out or wrapped in parentheses.
  • A member chain that fits stays on one line (Prettier breaks chains of three or more calls with function arguments). "Fits" means the whole chain, or its text up to a function argument at its end; a chain whose object argument is kept broken by object_wrap is broken before each . (Prettier keeps x.a().b().c({ on one line).
  • A destructuring pattern that fits stays on one line (Prettier breaks object patterns with nested object patterns).

Limitations

  • No quote normalization and no JSX layout. TypeScript has its own pack (typescript.md).
  • The grammar (tree-sitter-javascript 0.25) does not parse import assertions (import a from "./a.json" assert { type: "json" }, the syntax that with replaced), nor an arrow function with a block body followed by [ on the next line; such files are reported as syntax errors and left unchanged.
  • The lines of JSX and multi-line template literals keep their indentation, so they may be indented differently from the code around them after re-indentation.
  • Speed: about 1 s to format and 2 s to verify 100 KiB of ordinary code; minified bundles are about three times slower per byte (a 700 KiB bundle takes over a minute). Exclude generated bundles from formatting.