Format SQL from a real tokenizer, so a keyword inside a string literal stays a value, with five dialects whose string and identifier rules differ, comments kept, and the output checked by running it in sqlite
Initializing in your browser…
Format and validate XML without changing the document: CDATA, xml:space="preserve" and mixed content all survive, with errors carrying a line, a caret and a next step, plus XPath queries, a tree view and JSON conversion that names what JSON cannot carry
Format YAML without rewriting it: comments, anchors, quoting, block scalars, multi-document streams and the exact spelling of every number all survive, with YAML 1.1 and 1.2 both offered because the same file means different things under each
Minify or beautify JavaScript, TypeScript, CSS, HTML, JSON, XML and SQL with a real lexer per language, so a comment inside a string is not deleted and a line break the code depends on is kept, with the byte saving measured
Format a SQL statement into clauses and indentation, with the one rule that matters: the statement has to still do the same thing afterwards. That is measured rather than assumed. Every statement in the test corpus is executed by `sqlite3` before and after formatting and the rows are compared; the version this replaces kept the result on 13 of 18, and this one keeps it on all 18. Everything runs over a token stream, so a string literal is a token and nothing can reach inside one. The version this replaces ran over the raw text, and that is not a difference of style.
Measured on 2026-09-03, here is what the previous formatter did to ordinary SQL.
**It rewrote the data.** Keyword casing was one case-insensitive word-boundary replacement per keyword across the whole statement, so `SELECT note FROM t WHERE note = 'select from the menu'` came back with the note reading `SELECT FROM the menu`. The note in the database now says something else. A regular expression cannot tell a keyword from the same letters inside a quoted string; only a tokenizer can.
**It broke a value that spanned lines.** Collapsing every run of whitespace to one space turns a newline inside a string literal into a space, and the value changes. Breaking a line after every comma broke the one inside `'a, b'` too.
**It renamed columns.** `SELECT 1 AS "select"` came back with the quoted identifier rewritten, and a column really can be called select: quoting it is exactly how you say so.
**It edited comments.** A word inside `-- what this query is for` was upper-cased along with everything else.
**And it produced SQL that does not parse.** The rule that put `ON` on its own line had no word boundary, so `UNION ALL` became `UNI` followed by `ON ALL`, and sqlite refused the statement outright. Two of the eighteen corpus statements came back unrunnable.
This version tokenizes with `src/lib/text/sqlLex.ts`, whose per-dialect rules come from the same module the CSV tools use, already validated against real sqlite. **The dialect is a real setting now**, and it has to be, because it decides where a string ends: only MySQL treats a backslash inside a string literal as an escape; the identifier delimiter is a backtick in MySQL, a double quote in PostgreSQL, SQLite and Oracle and a square bracket in SQL Server; and PostgreSQL alone nests block comments and has dollar quoting. The dialect used to be stored in the URL with no control on the page and no effect on the output.
The syntax highlighting comes from the same tokens and is rendered as elements rather than as an HTML string, so a keyword inside a string is coloured as part of the string. The keyword COUNT is counted from the tokens too, so the words inside `'select from the menu'` do not inflate it. Identifier casing is offered but defaults to leaving it alone, and the page says why: PostgreSQL folds an unquoted identifier to lower case and Oracle to upper, so changing it is safe there and is not safe in a dialect that preserves it.
Paste a one-line query and get clauses, indentation and aligned brackets, with the confidence that the query still returns the same rows.
Apply one house style to keyword case, indentation and comma position, with the comments that explain the query left intact.
Switch the dialect to see where a backslash or a bracket changes what counts as a string or an identifier, which is where a formatter usually goes wrong.
It must not, and here that is measured rather than promised: every statement in the test corpus is executed by sqlite3 before and after formatting and the rows are compared. All 18 come back identical. The version this replaces managed 13, and two of the five it got wrong did not parse at all.
Because the dialect decides where a string literal ends. Only MySQL treats a backslash inside one as an escape; the identifier delimiter is a backtick in MySQL, a double quote in PostgreSQL, SQLite and Oracle and a square bracket in SQL Server; and PostgreSQL alone nests block comments and has dollar quoting. Get that wrong and a formatter can cut a string in half.
They are kept, on their own lines, and never re-cased. A comment is the only part of a query that says why it exists.
Because it is quoted, and quoting is how you say a name is exactly those characters. Rewriting a quoted identifier renames the column.
It can, and it is off by default with the reason stated. PostgreSQL folds an unquoted identifier to lower case and Oracle to upper, so changing it there is harmless; MySQL, SQLite and SQL Server preserve it, so changing it there can change which column a case-sensitive server sees. The page says which applies to the dialect you picked.
No. It formats what you give it and does not connect to a database or check that the query is valid. What it guarantees is that it does not make a valid query invalid, which is the property the corpus measures.
Your text is processed locally in the browser. Nothing you paste or open is transmitted or logged.