holywell 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "maxLineLength": 80,
3
+ "maxDepth": 200,
4
+ "maxInputSize": 10485760,
5
+ "strict": false,
6
+ "recover": true
7
+ }
@@ -0,0 +1,49 @@
1
+ # Code of Conduct
2
+
3
+ ## Our Pledge
4
+ We as members, contributors, and maintainers pledge to make participation in
5
+ this project a harassment-free experience for everyone, regardless of age, body
6
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
7
+ identity and expression, level of experience, education, socio-economic status,
8
+ nationality, personal appearance, race, religion, or sexual identity and
9
+ orientation.
10
+
11
+ ## Our Standards
12
+ Examples of behavior that contributes to a positive environment include:
13
+
14
+ - Demonstrating empathy and kindness toward other people.
15
+ - Being respectful of differing opinions, viewpoints, and experiences.
16
+ - Giving and gracefully accepting constructive feedback.
17
+ - Taking responsibility, apologizing to those affected by our mistakes, and
18
+ learning from the experience.
19
+ - Focusing on what is best not just for us as individuals, but for the overall
20
+ community.
21
+
22
+ Examples of unacceptable behavior include:
23
+
24
+ - The use of sexualized language or imagery, and sexual attention or advances.
25
+ - Trolling, insulting or derogatory comments, and personal or political attacks.
26
+ - Public or private harassment.
27
+ - Publishing others' private information, such as a physical or email address,
28
+ without their explicit permission.
29
+ - Other conduct which could reasonably be considered inappropriate in a
30
+ professional setting.
31
+
32
+ ## Enforcement Responsibilities
33
+ Project maintainers are responsible for clarifying and enforcing our standards
34
+ of acceptable behavior and will take appropriate and fair corrective action in
35
+ response to behavior that they deem inappropriate, threatening, offensive, or
36
+ harmful.
37
+
38
+ ## Scope
39
+ This Code of Conduct applies within all project spaces, and it also applies when
40
+ an individual is officially representing the project in public spaces.
41
+
42
+ ## Enforcement
43
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
44
+ reported to the project maintainers at `vincecoppola@gmail.com`.
45
+ All complaints will be reviewed and investigated promptly and fairly.
46
+
47
+ ## Attribution
48
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
49
+ version 2.1.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vince Coppola
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,538 @@
1
+ # holywell
2
+
3
+ [![npm version](https://img.shields.io/npm/v/holywell)](https://www.npmjs.com/package/holywell)
4
+ [![npm downloads](https://img.shields.io/npm/dm/holywell)](https://www.npmjs.com/package/holywell)
5
+ [![license](https://img.shields.io/npm/l/holywell)](https://github.com/vinsidious/holywell/blob/main/LICENSE)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/vinsidious/holywell/ci.yml?branch=main&label=CI)](https://github.com/vinsidious/holywell/actions)
7
+ [![coverage](https://img.shields.io/badge/coverage-regression%20suite-brightgreen)](https://github.com/vinsidious/holywell/tree/main/tests)
8
+
9
+ An opinionated SQL formatter that implements [Simon Holywell's SQL Style Guide](https://www.sqlstyle.guide/). It faithfully applies the guide's formatting rules -- including river alignment, keyword uppercasing, and consistent indentation -- to produce deterministic, readable SQL with minimal configuration.
10
+
11
+ > **Disclaimer:** This project is not officially associated with or endorsed by Simon Holywell or sqlstyle.guide. It is an independent, faithful implementation of the SQL formatting rules described in that style guide.
12
+
13
+ ## Quick Start
14
+
15
+ ### Install
16
+
17
+ ```bash
18
+ npm install holywell
19
+ ```
20
+
21
+ ### CLI Usage
22
+
23
+ ```bash
24
+ # Format a file
25
+ npx holywell query.sql
26
+
27
+ # Format all SQL files
28
+ npx holywell "**/*.sql"
29
+
30
+ # Check formatting (CI mode)
31
+ npx holywell --check "**/*.sql"
32
+
33
+ # Format in place
34
+ npx holywell --write "**/*.sql"
35
+ ```
36
+
37
+ ### Programmatic Usage
38
+
39
+ ```typescript
40
+ import { formatSQL } from 'holywell';
41
+
42
+ const formatted = formatSQL('select id, name from users where active = true;');
43
+ // Output:
44
+ // SELECT id, name
45
+ // FROM users
46
+ // WHERE active = TRUE;
47
+ ```
48
+
49
+ ## Table of Contents
50
+
51
+ - [What it does](#what-it-does)
52
+ - [When NOT to use holywell](#when-not-to-use-holywell)
53
+ - [SQL Dialect Support](#sql-dialect-support)
54
+ - [CLI Reference](#cli-reference)
55
+ - [API Guide](#api-guide)
56
+ - [How the formatter works](#how-the-formatter-works)
57
+ - [Edge Cases & Behavior](#edge-cases--behavior)
58
+ - [FAQ](#faq)
59
+ - [Documentation](#documentation)
60
+ - [Development](#development)
61
+ - [Performance](#performance)
62
+ - [Limitations](#limitations)
63
+ - [License](#license)
64
+
65
+ ## What it does
66
+
67
+ Takes messy SQL and formats it according to the [Simon Holywell SQL Style Guide](https://www.sqlstyle.guide/). A key technique from the guide is **river alignment** -- right-aligning keywords so content flows along a consistent vertical column:
68
+
69
+ ```sql
70
+ -- Input
71
+ select e.name, e.salary, d.department_name from employees as e inner join departments as d on e.department_id = d.department_id where e.salary > 50000 and d.department_name in ('Sales', 'Engineering') order by e.salary desc;
72
+
73
+ -- Output
74
+ SELECT e.name, e.salary, d.department_name
75
+ FROM employees AS e
76
+ INNER JOIN departments AS d
77
+ ON e.department_id = d.department_id
78
+ WHERE e.salary > 50000
79
+ AND d.department_name IN ('Sales', 'Engineering')
80
+ ORDER BY e.salary DESC;
81
+ ```
82
+
83
+ ### More examples
84
+
85
+ **Multi-table JOINs:**
86
+
87
+ ```sql
88
+ -- Input
89
+ select o.id, c.name, p.title, o.total from orders o join customers c on o.customer_id = c.id left join products p on o.product_id = p.id left join shipping s on o.id = s.order_id where o.created_at > '2024-01-01' and s.status = 'delivered' order by o.created_at desc;
90
+
91
+ -- Output
92
+ SELECT o.id, c.name, p.title, o.total
93
+ FROM orders AS o
94
+ JOIN customers AS c
95
+ ON o.customer_id = c.id
96
+
97
+ LEFT JOIN products AS p
98
+ ON o.product_id = p.id
99
+
100
+ LEFT JOIN shipping AS s
101
+ ON o.id = s.order_id
102
+ WHERE o.created_at > '2024-01-01'
103
+ AND s.status = 'delivered'
104
+ ORDER BY o.created_at DESC;
105
+ ```
106
+
107
+ **CTEs (Common Table Expressions):**
108
+
109
+ ```sql
110
+ -- Input
111
+ with monthly_totals as (select date_trunc('month', created_at) as month, sum(amount) as total from payments group by 1), running as (select month, total, sum(total) over (order by month) as cumulative from monthly_totals) select * from running where cumulative > 10000;
112
+
113
+ -- Output
114
+ WITH monthly_totals AS (
115
+ SELECT DATE_TRUNC('month', created_at) AS month,
116
+ SUM(amount) AS total
117
+ FROM payments
118
+ GROUP BY 1
119
+ ),
120
+ running AS (
121
+ SELECT month, total, SUM(total) OVER (ORDER BY month) AS cumulative
122
+ FROM monthly_totals
123
+ )
124
+ SELECT *
125
+ FROM running
126
+ WHERE cumulative > 10000;
127
+ ```
128
+
129
+ **Window functions:**
130
+
131
+ ```sql
132
+ -- Input
133
+ select department, employee, salary, rank() over (partition by department order by salary desc) as dept_rank, salary - avg(salary) over (partition by department) as diff_from_avg from employees;
134
+
135
+ -- Output
136
+ SELECT department,
137
+ employee,
138
+ salary,
139
+ RANK() OVER (PARTITION BY department
140
+ ORDER BY salary DESC) AS dept_rank,
141
+ salary - AVG(salary) OVER (PARTITION BY department) AS diff_from_avg
142
+ FROM employees;
143
+ ```
144
+
145
+ **CASE expressions:**
146
+
147
+ ```sql
148
+ -- Input
149
+ select name, case status when 'A' then 'Active' when 'I' then 'Inactive' when 'P' then 'Pending' else 'Unknown' end as status_label, case when balance > 10000 then 'high' when balance > 1000 then 'medium' else 'low' end as tier from accounts;
150
+
151
+ -- Output
152
+ SELECT name,
153
+ CASE status
154
+ WHEN 'A' THEN 'Active'
155
+ WHEN 'I' THEN 'Inactive'
156
+ WHEN 'P' THEN 'Pending'
157
+ ELSE 'Unknown'
158
+ END AS status_label,
159
+ CASE
160
+ WHEN balance > 10000 THEN 'high'
161
+ WHEN balance > 1000 THEN 'medium'
162
+ ELSE 'low'
163
+ END AS tier
164
+ FROM accounts;
165
+ ```
166
+
167
+ ## When NOT to use holywell
168
+
169
+ - **You need highly configurable style output** -- holywell intentionally does not expose style knobs for indentation strategy, keyword casing, or alignment mode. If you need full style customization, use [sql-formatter](https://github.com/sql-formatter-org/sql-formatter) or [prettier-plugin-sql](https://github.com/JounQin/prettier-plugin-sql).
170
+ - **You exclusively target MySQL or SQL Server** -- holywell is PostgreSQL-first. Standard ANSI SQL works fine, but vendor-specific syntax (stored procedures, MySQL-only functions) may not be fully parsed.
171
+ - **You need a language server** -- holywell is a formatter, not a linter or LSP. It does not provide diagnostics, completions, or semantic analysis.
172
+
173
+ ## SQL Dialect Support
174
+
175
+ | Dialect | Status | Notes |
176
+ |---|---|---|
177
+ | PostgreSQL | Primary / continuously tested | Full formatter/parser coverage target |
178
+ | ANSI SQL core | Broad support | Most query/DDL patterns covered |
179
+ | MySQL | Partial | Many ANSI queries work; MySQL-specific extensions may recover as raw |
180
+ | SQL Server (T-SQL) | Partial | Many ANSI queries work; procedural T-SQL is limited |
181
+ | SQLite | Partial | Common ANSI queries work; SQLite-specific extensions are limited |
182
+
183
+ holywell test coverage is PostgreSQL-first. If you rely on non-PostgreSQL vendor extensions, run `--check` in CI and prefer `--strict` where parse failures should block merges.
184
+
185
+ You can extend keyword/clause recognition without forking:
186
+
187
+ ```typescript
188
+ import { formatSQL } from 'holywell';
189
+
190
+ const formatted = formatSQL(sql, {
191
+ dialect: {
192
+ additionalKeywords: ['QUALIFY', 'TOP'],
193
+ clauseKeywords: ['QUALIFY'],
194
+ },
195
+ });
196
+ ```
197
+
198
+ ### PostgreSQL (Full Support)
199
+
200
+ - Type casts (`::integer`), JSON operators (`->`, `->>`), dollar-quoting (`$$...$$`)
201
+ - Array constructors, window functions, CTEs, LATERAL joins
202
+ - ON CONFLICT (UPSERT), RETURNING clauses
203
+ - **Note:** PL/pgSQL function bodies are preserved verbatim (not reformatted)
204
+
205
+ ### ANSI SQL (Full Support)
206
+
207
+ - SELECT, INSERT, UPDATE, DELETE, MERGE
208
+ - JOINs (INNER, LEFT, RIGHT, FULL, CROSS, NATURAL)
209
+ - CTEs (WITH, WITH RECURSIVE)
210
+ - Window functions (PARTITION BY, ORDER BY, frame clauses)
211
+ - DDL (CREATE TABLE, ALTER TABLE, DROP, CREATE INDEX, CREATE VIEW)
212
+
213
+ ### MySQL (Partial)
214
+
215
+ - Standard ANSI SQL queries format correctly
216
+ - Backtick identifiers, LIMIT offset syntax, and storage engine clauses are not yet supported
217
+
218
+ ### SQL Server (Partial)
219
+
220
+ - Standard ANSI SQL queries format correctly
221
+ - T-SQL procedural syntax (BEGIN/END blocks, DECLARE, @@variables) is not yet supported
222
+
223
+ ### Recovery Mode
224
+
225
+ Unsupported syntax is passed through unchanged rather than causing errors. Use `--strict` to fail on unparseable SQL.
226
+
227
+ ## Style Guide
228
+
229
+ This formatter implements the [Simon Holywell SQL Style Guide](https://www.sqlstyle.guide/). Key principles from the guide that holywell enforces:
230
+
231
+ - **River alignment** -- Clause/logical keywords are right-aligned to a per-statement river width derived from the longest top-level aligned keyword
232
+ - **Keyword uppercasing** -- Reserved words like `SELECT`, `FROM`, `WHERE` are uppercased
233
+ - **Identifier normalization** -- Most unquoted identifiers are lowercased; quoted identifiers are preserved
234
+ - **Right-aligned clause/logical keywords** -- `SELECT`, `FROM`, `WHERE`, `AND`, `OR`, `JOIN`, `ON`, `ORDER BY`, `GROUP BY`, etc. align within each formatted block
235
+ - **Consistent indentation** -- Continuation lines and subexpressions are indented predictably
236
+
237
+ For the full style guide, see [sqlstyle.guide](https://www.sqlstyle.guide/) or the [source on GitHub](https://github.com/treffynnon/sqlstyle.guide).
238
+
239
+ ## Why holywell?
240
+
241
+ | | **holywell** | sql-formatter | prettier-plugin-sql |
242
+ |---|---|---|---|
243
+ | **Formatting style** | River alignment ([sqlstyle.guide](https://www.sqlstyle.guide/)) | Indentation-based | Indentation-based |
244
+ | **Configuration** | Opinionated defaults + small operational config (`.holywellrc.json`) | Configurable | Configurable via Prettier |
245
+ | **PostgreSQL support** | First-class (casts, JSON ops, dollar-quoting, arrays) | Partial | Partial |
246
+ | **Runtime dependencies** | Zero | Several | Prettier + parser |
247
+ | **Idempotent** | Yes | Yes | Yes |
248
+ | **Keyword casing** | Uppercase (enforced) | Configurable | Configurable |
249
+ | **Identifier casing** | Lowercase (enforced) | Not modified | Not modified |
250
+ | **Output** | Deterministic, single style | Depends on config | Depends on config |
251
+
252
+ holywell is the right choice when you want consistent, readable SQL with minimal setup and deterministic style.
253
+
254
+ ### Configuration philosophy
255
+
256
+ holywell keeps style deterministic by design: no indentation/casing style matrix, no formatter presets.
257
+ It does support a focused optional config file (`.holywellrc.json`) for operational settings:
258
+
259
+ - `maxLineLength`
260
+ - `maxDepth`
261
+ - `maxInputSize`
262
+ - `strict`
263
+ - `recover`
264
+
265
+ CLI flags still override config values.
266
+
267
+ A starter config is available at `.holywellrc.json.example`.
268
+
269
+ ## CLI Reference
270
+
271
+ ```bash
272
+ # Format a file (prints to stdout by default)
273
+ npx holywell query.sql
274
+
275
+ # Format a file in place
276
+ npx holywell --write query.sql
277
+
278
+ # Format from stdin
279
+ cat query.sql | npx holywell
280
+
281
+ # Check if a file is already formatted (exits non-zero if not)
282
+ npx holywell --check query.sql
283
+
284
+ # List files that would change (useful in CI)
285
+ npx holywell --list-different "src/**/*.sql"
286
+ npx holywell -l "migrations/*.sql"
287
+
288
+ # Strict mode: fail on unparseable SQL instead of passing through
289
+ npx holywell --strict --check "**/*.sql"
290
+
291
+ # Tune output width
292
+ npx holywell --max-line-length 100 query.sql
293
+
294
+ # Use project config
295
+ npx holywell --config .holywellrc.json --check "**/*.sql"
296
+
297
+ # Ignore files (can repeat --ignore)
298
+ npx holywell --check --ignore "migrations/**" "**/*.sql"
299
+
300
+ # Or store ignore patterns in .holywellignore (one pattern per line)
301
+ npx holywell --check "**/*.sql"
302
+
303
+ # Control color in CI/logs
304
+ npx holywell --color=always --check query.sql
305
+
306
+ # Generate shell completion
307
+ npx holywell --completion bash
308
+ npx holywell --completion zsh
309
+ npx holywell --completion fish
310
+
311
+ # Pipe patterns
312
+ pbpaste | npx holywell | pbcopy # Format clipboard (macOS)
313
+ pg_dump mydb --schema-only | npx holywell > schema.sql
314
+ echo "select 1" | npx holywell
315
+ ```
316
+
317
+ By default, `npx holywell query.sql` prints formatted output to **stdout**. Use `--write` to modify the file in place.
318
+
319
+ When present, `.holywellignore` is read from the current working directory and combined with any `--ignore` flags.
320
+
321
+ **CLI exit codes:**
322
+
323
+ | Code | Meaning |
324
+ |------|---------|
325
+ | `0` | Success (or all files already formatted with `--check`) |
326
+ | `1` | Check failure |
327
+ | `2` | Parse or tokenize error |
328
+ | `3` | Usage or I/O error |
329
+
330
+ ## API Guide
331
+
332
+ ### Basic Usage
333
+
334
+ ```typescript
335
+ import { formatSQL } from 'holywell';
336
+
337
+ const formatted = formatSQL('SELECT * FROM users;');
338
+ ```
339
+
340
+ ### Synchronous API by design
341
+
342
+ `formatSQL`, `parse`, and `tokenize` are intentionally synchronous.
343
+ This keeps editor/CLI integration predictable and avoids hidden async overhead.
344
+
345
+ ### Error Recovery
346
+
347
+ By default, unparseable SQL is passed through unchanged:
348
+
349
+ ```typescript
350
+ const warnings: string[] = [];
351
+ const formatted = formatSQL(sql, {
352
+ onRecover: (error, raw) => {
353
+ warnings.push(`Line ${error.token.line}: ${error.message}`);
354
+ }
355
+ });
356
+ ```
357
+
358
+ ### Strict Mode (throw on parse errors)
359
+
360
+ ```typescript
361
+ import { formatSQL, ParseError } from 'holywell';
362
+
363
+ try {
364
+ formatSQL(sql, { recover: false });
365
+ } catch (err) {
366
+ if (err instanceof ParseError) {
367
+ console.error(`Parse error: ${err.message}`);
368
+ }
369
+ }
370
+ ```
371
+
372
+ ### Depth Limits
373
+
374
+ ```typescript
375
+ formatSQL(sql, { maxDepth: 300 }); // Increase for deeply nested CTEs
376
+ ```
377
+
378
+ ### Input Size Limits
379
+
380
+ ```typescript
381
+ formatSQL(sql, { maxInputSize: 5_000_000 }); // 5MB limit (default: 10MB)
382
+ ```
383
+
384
+ ### Low-Level Access
385
+
386
+ ```typescript
387
+ import { tokenize, parse, formatStatements, visitAst } from 'holywell';
388
+
389
+ // Tokenize SQL into a token stream
390
+ const tokens = tokenize(sql);
391
+
392
+ // Parse SQL into an AST
393
+ const ast = parse(sql);
394
+
395
+ // Format AST nodes back to SQL
396
+ const output = formatStatements(ast);
397
+
398
+ // Visit AST nodes (for custom linting/analysis)
399
+ visitAst(ast, {
400
+ byType: {
401
+ select(node) {
402
+ console.log('SELECT node:', node);
403
+ },
404
+ },
405
+ });
406
+ ```
407
+
408
+ ### Error Types
409
+
410
+ ```typescript
411
+ import { formatSQL, TokenizeError, ParseError, MaxDepthError } from 'holywell';
412
+
413
+ try {
414
+ const result = formatSQL(input);
415
+ } catch (err) {
416
+ if (err instanceof TokenizeError) {
417
+ // Invalid token encountered during lexing (e.g., unterminated string)
418
+ console.error(`Tokenize error at position ${err.position}: ${err.message}`);
419
+ } else if (err instanceof MaxDepthError) {
420
+ // Parser nesting exceeded configured maxDepth
421
+ console.error(`Parse depth exceeded: ${err.message}`);
422
+ } else if (err instanceof ParseError) {
423
+ // Structural error in the SQL (e.g., unmatched parentheses)
424
+ console.error(`Parse error: ${err.message}`);
425
+ } else if (err instanceof Error && err.message.includes('Input exceeds maximum size')) {
426
+ // Input exceeded maxInputSize
427
+ console.error(`Input too large: ${err.message}`);
428
+ } else {
429
+ throw err;
430
+ }
431
+ }
432
+ ```
433
+
434
+ ## How the formatter works
435
+
436
+ ```
437
+ SQL Text → Tokenizer → Parser → AST → Formatter → Formatted SQL
438
+ ```
439
+
440
+ 1. **Tokenizer** (`src/tokenizer.ts`) -- Splits SQL text into tokens (keywords, identifiers, literals, operators, comments)
441
+ 2. **Parser** (`src/parser.ts`) -- Builds an AST from the token stream
442
+ 3. **Formatter** (`src/formatter.ts`) -- Walks the AST and produces formatted output
443
+
444
+ The key formatting concept is the **river**. For each statement, holywell derives a river width from the longest top-level aligned keyword in that statement (for example, `RETURNING` can widen DML alignment). Clause/logical keywords are then right-aligned to that width so content starts in a consistent column. Nested blocks may use their own derived widths. This approach comes directly from the [Simon Holywell SQL Style Guide](https://www.sqlstyle.guide/).
445
+
446
+ ## Edge Cases & Behavior
447
+
448
+ ### Long Lines
449
+
450
+ holywell targets 80-column output by default and supports `maxLineLength` (CLI flag or config file). It does not break individual tokens (identifiers, string literals), so single-token lines can still exceed the configured width.
451
+
452
+ ### Comment Preservation
453
+
454
+ Line comments and block comments are preserved. Comments attached to specific expressions maintain their association.
455
+
456
+ ### Keyword Casing
457
+
458
+ All SQL keywords are uppercased. Identifiers are preserved as-is (quoted identifiers keep their case and quotes). Unquoted identifiers are lowercased.
459
+
460
+ ### Idempotency
461
+
462
+ Formatting is idempotent: `formatSQL(formatSQL(x)) === formatSQL(x)` for all valid inputs.
463
+
464
+ ## FAQ
465
+
466
+ **Q: Can I change the indentation style or keyword casing?**
467
+
468
+ No. Style output is intentionally fixed. holywell provides operational configuration (line length, strictness/safety), not style customization.
469
+
470
+ **Q: What happens with SQL syntax holywell doesn't understand?**
471
+
472
+ In default (recovery) mode, unrecognized statements are passed through unchanged. Use `--strict` to fail instead.
473
+
474
+ **Q: How fast is holywell?**
475
+
476
+ ~30,000+ statements/second on modern hardware. A typical migration file formats in <10ms.
477
+
478
+ **Q: Does holywell modify SQL semantics?**
479
+
480
+ No. holywell changes whitespace, uppercases SQL keywords, lowercases unquoted identifiers, and normalizes alias syntax (e.g., inserting AS). Quoted identifiers and string literals are preserved exactly. The semantic meaning is preserved.
481
+
482
+ **Q: Does holywell respect `.editorconfig`?**
483
+
484
+ No. holywell does not read `.editorconfig`. It does read `.holywellrc.json` (or `--config`) for operational settings, but style output remains deterministic.
485
+
486
+ **Q: Can I customize the river width?**
487
+
488
+ Not directly. River width is derived automatically from statement structure. You can influence wrapping via `maxLineLength`, but keyword alignment behavior itself is fixed.
489
+
490
+ **Q: Does formatting change SQL semantics?**
491
+
492
+ holywell only changes whitespace and casing. Specifically: SQL keywords are uppercased (`select` becomes `SELECT`), unquoted identifiers are lowercased (`MyTable` becomes `mytable`), and quoted identifiers are preserved exactly (`"MyTable"` stays `"MyTable"`). If your database is case-sensitive for unquoted identifiers (rare, but possible), see the [Migration Guide](docs/migration-guide.md) for details.
493
+
494
+ **Q: Does holywell work with MySQL / SQL Server / SQLite?**
495
+
496
+ holywell is PostgreSQL-first, but any query written in standard ANSI SQL will format correctly regardless of your target database. Vendor-specific extensions (stored procedures, MySQL-only syntax) may not be fully parsed. See [SQL Dialect Support](#sql-dialect-support) for details.
497
+
498
+ ## Documentation
499
+
500
+ - [Integrations](docs/integrations.md) -- Pre-commit hooks, CI pipelines, and editor setup recipes
501
+ - [Architecture](docs/architecture.md) -- Internal pipeline and design decisions
502
+ - [Style Guide Mapping](docs/style-guide.md) -- How holywell maps to each rule in the Simon Holywell SQL Style Guide
503
+ - [Migration Guide](docs/migration-guide.md) -- Rolling out holywell in existing codebases with minimal churn
504
+ - [Contributing](CONTRIBUTING.md) -- Development setup, running tests, and submitting changes
505
+ - [Changelog](CHANGELOG.md) -- Release history
506
+
507
+ ## Development
508
+
509
+ Requires [Bun](https://bun.sh/).
510
+
511
+ ```bash
512
+ # Install dependencies
513
+ bun install
514
+
515
+ # Run tests
516
+ bun test
517
+
518
+ # Type check
519
+ bun run check
520
+
521
+ # Build dist (for npm publishing)
522
+ bun run build
523
+ ```
524
+
525
+ ## Performance
526
+
527
+ holywell has zero runtime dependencies and formats SQL through a single tokenize-parse-format pass. Typical throughput is 30,000+ statements per second on modern hardware. Input is bounded by default size limits to prevent excessive memory use on untrusted input.
528
+
529
+ ## Limitations
530
+
531
+ - Dialect coverage is broad but intentionally pragmatic, with strongest support for PostgreSQL-style syntax.
532
+ - Procedural SQL bodies (`CREATE FUNCTION ... LANGUAGE plpgsql` control-flow blocks, vendor-specific scripting extensions) are not fully parsed as procedural ASTs.
533
+ - Unknown/unsupported constructs may fall back to raw statement preservation.
534
+ - Formatting style is opinionated and focused on faithfully implementing the [Simon Holywell SQL Style Guide](https://www.sqlstyle.guide/) rather than per-project style configurability.
535
+
536
+ ## License
537
+
538
+ MIT