prettier-plugin-tsql 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/bin/dotnet/Microsoft.SqlServer.TransactSql.ScriptDom.dll +0 -0
- package/bin/dotnet/SqlScriptDom.deps.json +41 -0
- package/bin/dotnet/SqlScriptDom.dll +0 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/language.d.ts +3 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +9 -0
- package/dist/language.js.map +1 -0
- package/dist/options.d.ts +3 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +35 -0
- package/dist/options.js.map +1 -0
- package/dist/parser/index.d.ts +5 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +263 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/types.d.ts +21 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js +2 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/printer/admin.d.ts +22 -0
- package/dist/printer/admin.d.ts.map +1 -0
- package/dist/printer/admin.js +250 -0
- package/dist/printer/admin.js.map +1 -0
- package/dist/printer/ddl.d.ts +36 -0
- package/dist/printer/ddl.d.ts.map +1 -0
- package/dist/printer/ddl.js +836 -0
- package/dist/printer/ddl.js.map +1 -0
- package/dist/printer/expressions.d.ts +11 -0
- package/dist/printer/expressions.d.ts.map +1 -0
- package/dist/printer/expressions.js +1475 -0
- package/dist/printer/expressions.js.map +1 -0
- package/dist/printer/helpers.d.ts +25 -0
- package/dist/printer/helpers.d.ts.map +1 -0
- package/dist/printer/helpers.js +61 -0
- package/dist/printer/helpers.js.map +1 -0
- package/dist/printer/index.d.ts +4 -0
- package/dist/printer/index.d.ts.map +1 -0
- package/dist/printer/index.js +30 -0
- package/dist/printer/index.js.map +1 -0
- package/dist/printer/procedural.d.ts +41 -0
- package/dist/printer/procedural.d.ts.map +1 -0
- package/dist/printer/procedural.js +364 -0
- package/dist/printer/procedural.js.map +1 -0
- package/dist/printer/security.d.ts +15 -0
- package/dist/printer/security.d.ts.map +1 -0
- package/dist/printer/security.js +309 -0
- package/dist/printer/security.js.map +1 -0
- package/dist/printer/statements.d.ts +18 -0
- package/dist/printer/statements.d.ts.map +1 -0
- package/dist/printer/statements.js +689 -0
- package/dist/printer/statements.js.map +1 -0
- package/dist/printer/utils.d.ts +34 -0
- package/dist/printer/utils.d.ts.map +1 -0
- package/dist/printer/utils.js +61 -0
- package/dist/printer/utils.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mike Schall
|
|
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,199 @@
|
|
|
1
|
+
# prettier-plugin-tsql
|
|
2
|
+
|
|
3
|
+
> **Alpha quality — do not use in production. Use at your own risk.**
|
|
4
|
+
|
|
5
|
+
A [Prettier](https://prettier.io) plugin that formats T-SQL (SQL Server) using Microsoft's official `Microsoft.SqlServer.TransactSql.ScriptDom` parser — the same parser SQL Server itself uses.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
Parses T-SQL via the official ScriptDom library (no hand-rolled grammar). Configurable keyword casing, layout density, and comma style. Preserves `--` and `/* */` comments — trailing, leading, inside procedure bodies, between parameters and `AS`. Emits `go` batch separators where required. Integrates with editor extensions that support Prettier (VS Code, etc.).
|
|
10
|
+
|
|
11
|
+
**DML**
|
|
12
|
+
|
|
13
|
+
- `SELECT`, `INSERT`, `UPDATE`, `DELETE`
|
|
14
|
+
- `MERGE INTO … USING … ON … WHEN MATCHED/NOT MATCHED`
|
|
15
|
+
- `OUTPUT` / `OUTPUT INTO` on `INSERT`, `UPDATE`, `DELETE`, and `MERGE` (including `$action`, `inserted.*`, `deleted.*`)
|
|
16
|
+
- CTEs, `UNION`/`UNION ALL`, subqueries, derived tables
|
|
17
|
+
- `CASE` expressions (simple and searched), `IN`/`NOT IN`
|
|
18
|
+
- `TOP (n)`, `TOP (n) PERCENT`, `TOP (n) WITH TIES`
|
|
19
|
+
- `PIVOT` / `UNPIVOT`
|
|
20
|
+
- `FOR XML` (`AUTO`, `PATH`, `RAW`, `EXPLICIT`, `XMLSCHEMA`, `ELEMENTS`, `ROOT`, `TYPE`, etc.) and `FOR JSON` (`AUTO`, `PATH`, `ROOT`, `INCLUDE_NULL_VALUES`, `WITHOUT_ARRAY_WRAPPER`)
|
|
21
|
+
- `TABLESAMPLE [SYSTEM] (n PERCENT|ROWS) [REPEATABLE(seed)]`
|
|
22
|
+
- Temporal table queries — `FOR SYSTEM_TIME AS OF`, `FROM … TO`, `BETWEEN … AND`, `CONTAINED IN`, `ALL`
|
|
23
|
+
- Joins: `INNER`, `LEFT`, `RIGHT`, `FULL OUTER`, `CROSS JOIN`, `CROSS APPLY`, `OUTER APPLY`; multiple joins, multi-predicate `ON`, self-joins, parenthesized joins, derived table joins
|
|
24
|
+
- Table-valued functions (TVFs) in `FROM` clauses; table hints (`WITH (NOLOCK)`, etc.)
|
|
25
|
+
- Window functions with `OVER` clause — `PARTITION BY`, `ORDER BY`, full frame support (`ROWS`/`RANGE BETWEEN … AND …`, `UNBOUNDED PRECEDING/FOLLOWING`, `CURRENT ROW`); `IGNORE NULLS`/`RESPECT NULLS`; named window references; named `WINDOW` clause
|
|
26
|
+
- Ordered set aggregates: `WITHIN GROUP (ORDER BY …)` for `STRING_AGG`, `PERCENTILE_CONT`/`PERCENTILE_DISC`, etc.
|
|
27
|
+
- Expression functions: `CAST`, `CONVERT`, `TRY_CAST`, `TRY_CONVERT` (with full data type including length/precision), `IIF`, `COALESCE`, `NULLIF`, `AT TIME ZONE`, `IS [NOT] DISTINCT FROM`, `TRIM(LEADING|TRAILING|BOTH …)`, `PARSE`, `TRY_PARSE`
|
|
28
|
+
- Sequence expressions: `NEXT VALUE FOR sequence [OVER (…)]`
|
|
29
|
+
- JSON functions: `JSON_OBJECT(key: value, …)`, `JSON_ARRAY(…)`, `JSON_ARRAYAGG(… ORDER BY …)` with `ABSENT|NULL ON NULL`
|
|
30
|
+
- Full-text predicates: `CONTAINS`/`FREETEXT` (single column, multi-column, wildcard, `LANGUAGE`); `CONTAINSTABLE`/`FREETEXTTABLE` as join sources
|
|
31
|
+
- Rowset functions: `OPENJSON` and `OPENXML` with `WITH` schema declarations; `OPENJSON` row-path and `AS JSON` columns; `OPENROWSET` provider and `OPENROWSET(BULK …)` forms
|
|
32
|
+
- Built-in functions formatted as standard function calls: `GREATEST`, `LEAST`, `DATE_BUCKET`, `DATETRUNC`, `GENERATE_SERIES`, `LEFT_SHIFT`, `RIGHT_SHIFT`, `BIT_COUNT`, `GET_BIT`, `SET_BIT`, `APPROX_PERCENTILE_CONT`, `APPROX_PERCENTILE_DISC`, `JSON_PATH_EXISTS`, `STRING_SPLIT`, `ISJSON`, `LTRIM`, `RTRIM`
|
|
33
|
+
|
|
34
|
+
**DDL**
|
|
35
|
+
|
|
36
|
+
- `CREATE TABLE` (columns, constraints, computed columns `AS expr [PERSISTED]`, `WITH` options such as `DATA_COMPRESSION`, `MEMORY_OPTIMIZED`), `ALTER TABLE` (ADD/DROP column, ADD/DROP/ENABLE/DISABLE constraint), `CREATE INDEX` (UNIQUE/CLUSTERED/NONCLUSTERED, ASC/DESC, INCLUDE), `ALTER INDEX … REBUILD/REORGANIZE/DISABLE`
|
|
37
|
+
- `CREATE/ALTER/CREATE OR ALTER PROCEDURE`, `CREATE/ALTER/CREATE OR ALTER FUNCTION`, `CREATE/ALTER/CREATE OR ALTER VIEW`
|
|
38
|
+
- `CREATE/ALTER TRIGGER` (DML triggers: AFTER/INSTEAD OF INSERT/UPDATE/DELETE)
|
|
39
|
+
- `CREATE/ALTER/DROP SEQUENCE` with full options (START WITH, INCREMENT BY, MINVALUE/NO MINVALUE, MAXVALUE/NO MAXVALUE, CYCLE/NO CYCLE, CACHE/NO CACHE)
|
|
40
|
+
- `BULK INSERT … FROM … WITH (options)`
|
|
41
|
+
- `CREATE TYPE … FROM …` (scalar UDDTs) and `CREATE TYPE … AS TABLE (…)` (table-valued parameters)
|
|
42
|
+
- `CREATE SYNONYM` / `DROP SYNONYM` (with `IF EXISTS`)
|
|
43
|
+
- `CREATE SCHEMA` (with optional `AUTHORIZATION`), `ALTER SCHEMA … TRANSFER` (plain objects, `TYPE::`, `XML SCHEMA COLLECTION::`), `DROP SCHEMA` (with `IF EXISTS`)
|
|
44
|
+
- `CREATE PARTITION FUNCTION` (RANGE LEFT/RIGHT, boundary values), `ALTER PARTITION FUNCTION` (SPLIT/MERGE RANGE), `DROP PARTITION FUNCTION`
|
|
45
|
+
- `CREATE PARTITION SCHEME` (AS PARTITION, ALL TO / TO filegroup list), `ALTER PARTITION SCHEME` (NEXT USED), `DROP PARTITION SCHEME`
|
|
46
|
+
- `DROP TABLE/PROCEDURE/VIEW/FUNCTION/INDEX/TRIGGER/SEQUENCE/SYNONYM/SCHEMA` (with `IF EXISTS`)
|
|
47
|
+
- `DROP DATABASE` (with `IF EXISTS`, multiple databases)
|
|
48
|
+
- `CREATE DATABASE` (with optional `COLLATE`, file group specs, snapshot)
|
|
49
|
+
- `ALTER DATABASE` — `SET` (any option with proper keyword reconstruction), `COLLATE`, `MODIFY NAME`, `ADD/REMOVE FILE`, `ADD/REMOVE FILEGROUP`, `MODIFY FILE`, `MODIFY FILEGROUP`, `REBUILD LOG`, `SCOPED CONFIGURATION SET/CLEAR`
|
|
50
|
+
|
|
51
|
+
**Database Administration**
|
|
52
|
+
|
|
53
|
+
- `DBCC` commands — any command name, literal arguments, `WITH` options
|
|
54
|
+
- `BACKUP DATABASE` / `BACKUP LOG` — `TO DISK/TAPE/URL`, `MIRROR TO`, `WITH` options
|
|
55
|
+
- `RESTORE DATABASE` / `RESTORE LOG` / `RESTORE FILELISTONLY` / `RESTORE HEADERONLY` / `RESTORE VERIFYONLY` — `FROM DISK/TAPE/URL`, `WITH` options
|
|
56
|
+
|
|
57
|
+
**Procedural / Control Flow**
|
|
58
|
+
|
|
59
|
+
- `USE`, `SET NOCOUNT/ANSI_NULLS/QUOTED_IDENTIFIER/XACT_ABORT/…` ON/OFF, `SET IDENTITY_INSERT`, `SET TRANSACTION ISOLATION LEVEL`, `SET STATISTICS`, `WAITFOR DELAY/TIME`
|
|
60
|
+
- `DECLARE`, `SET @var`, `SET ROWCOUNT`, `PRINT`, `RETURN`, `EXECUTE`, `TRUNCATE TABLE`
|
|
61
|
+
- `IF`/`ELSE`, `WHILE`, `BREAK`, `CONTINUE`, `GOTO`/label, `THROW`, `RAISERROR`, `TRY/CATCH`
|
|
62
|
+
- `BEGIN`/`COMMIT`/`ROLLBACK TRANSACTION`
|
|
63
|
+
- `DECLARE CURSOR` / `OPEN` / `FETCH NEXT/PRIOR/FIRST/LAST/ABSOLUTE/RELATIVE` / `CLOSE` / `DEALLOCATE`
|
|
64
|
+
- `EXECUTE AS` (CALLER / USER / LOGIN / SELF / OWNER, with `WITH NO REVERT`) / `REVERT`
|
|
65
|
+
- `CREATE/ALTER PROCEDURE` and `CREATE/ALTER FUNCTION` `WITH` options: `ENCRYPTION`, `RECOMPILE`, `EXECUTE AS`
|
|
66
|
+
|
|
67
|
+
**Security**
|
|
68
|
+
|
|
69
|
+
- `GRANT` / `DENY` / `REVOKE` — all securable classes (OBJECT, SCHEMA, DATABASE, SERVER, LOGIN, USER, ROLE, ASSEMBLY, …), column lists, WITH GRANT OPTION, CASCADE, GRANT OPTION FOR, AS clause, multiple principals
|
|
70
|
+
- `CREATE/ALTER/DROP USER` — FOR LOGIN, WITHOUT LOGIN, FROM EXTERNAL PROVIDER, WITH options
|
|
71
|
+
- `CREATE/ALTER/DROP LOGIN` — password (HASHED/MUST_CHANGE), FROM WINDOWS, FROM CERTIFICATE/ASYMMETRIC KEY, ENABLE/DISABLE, ADD/DROP CREDENTIAL
|
|
72
|
+
- `CREATE/ALTER/DROP ROLE` — AUTHORIZATION owner, ADD/DROP MEMBER, WITH NAME rename
|
|
73
|
+
|
|
74
|
+
## Pending implementation
|
|
75
|
+
|
|
76
|
+
The constructs below are parsed correctly but emitted as-is (original source text preserved). Open a ticket to request formatting support for any of these.
|
|
77
|
+
|
|
78
|
+
### DDL object model
|
|
79
|
+
|
|
80
|
+
- Ledger table syntax — `CREATE TABLE ... WITH (LEDGER = ON, ...)` table options
|
|
81
|
+
- Assemblies (`CREATE/ALTER/DROP ASSEMBLY`)
|
|
82
|
+
- XML schema collections (`CREATE/ALTER/DROP XML SCHEMA COLLECTION`)
|
|
83
|
+
- Full-text catalogs and indexes (`CREATE/ALTER/DROP FULLTEXT CATALOG`, `CREATE/ALTER/DROP FULLTEXT INDEX`)
|
|
84
|
+
|
|
85
|
+
### Service Broker
|
|
86
|
+
|
|
87
|
+
- `CREATE/ALTER/DROP QUEUE`, `SEND`, `RECEIVE`, `CREATE/ALTER/DROP SERVICE`, `CREATE/ALTER/DROP CONTRACT`, `CREATE/ALTER/DROP MESSAGE TYPE`, `CREATE/ALTER/DROP ROUTE`
|
|
88
|
+
|
|
89
|
+
### Extended Events
|
|
90
|
+
|
|
91
|
+
- `CREATE/ALTER/DROP EVENT SESSION`
|
|
92
|
+
|
|
93
|
+
### Cryptography
|
|
94
|
+
|
|
95
|
+
- `CREATE/ALTER/DROP CERTIFICATE`, `CREATE/ALTER/DROP SYMMETRIC KEY`, `CREATE/ALTER/DROP ASYMMETRIC KEY`, `OPEN/CLOSE MASTER KEY`
|
|
96
|
+
|
|
97
|
+
### High Availability
|
|
98
|
+
|
|
99
|
+
- `CREATE/ALTER/DROP AVAILABILITY GROUP`, `CREATE/ALTER/DROP ENDPOINT`
|
|
100
|
+
|
|
101
|
+
### External Data
|
|
102
|
+
|
|
103
|
+
- `CREATE/ALTER/DROP EXTERNAL TABLE`, `CREATE/ALTER/DROP EXTERNAL DATA SOURCE`, `CREATE/ALTER/DROP EXTERNAL FILE FORMAT`, `CREATE/ALTER/DROP EXTERNAL RESOURCE POOL`
|
|
104
|
+
|
|
105
|
+
### Audit
|
|
106
|
+
|
|
107
|
+
- `CREATE/ALTER/DROP SERVER AUDIT`, `CREATE/ALTER/DROP DATABASE AUDIT SPECIFICATION`, `CREATE/ALTER/DROP SERVER AUDIT SPECIFICATION`
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Node.js 20+
|
|
112
|
+
- [.NET 8+ Runtime](https://dotnet.microsoft.com/download/dotnet/8.0) (the SDK is only needed for building from source)
|
|
113
|
+
- Prettier 3.x (peer dependency)
|
|
114
|
+
|
|
115
|
+
## Installation
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install --save-dev prettier-plugin-tsql prettier
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
> The package ships with the compiled .NET DLL — no separate `dotnet` build step is needed when installing from npm.
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
Add the plugin to your Prettier config:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
// prettier.config.js
|
|
129
|
+
export default {
|
|
130
|
+
plugins: ['prettier-plugin-tsql'],
|
|
131
|
+
sqlKeywordCase: 'lower',
|
|
132
|
+
sqlDensity: 'standard',
|
|
133
|
+
sqlCommaStyle: 'trailing',
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Or in `.prettierrc`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"plugins": ["prettier-plugin-tsql"],
|
|
142
|
+
"sqlKeywordCase": "lower",
|
|
143
|
+
"sqlDensity": "standard",
|
|
144
|
+
"sqlCommaStyle": "trailing"
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Options
|
|
149
|
+
|
|
150
|
+
| Option | Default | Choices |
|
|
151
|
+
| ---------------- | ---------- | --------------------------------- |
|
|
152
|
+
| `sqlKeywordCase` | `lower` | `upper`, `lower`, `preserve` |
|
|
153
|
+
| `sqlDensity` | `standard` | `compact`, `standard`, `spacious` |
|
|
154
|
+
| `sqlCommaStyle` | `trailing` | `trailing`, `leading` |
|
|
155
|
+
|
|
156
|
+
See [docs/options.md](docs/options.md) for full details and examples.
|
|
157
|
+
|
|
158
|
+
## Quick Example
|
|
159
|
+
|
|
160
|
+
**Input**
|
|
161
|
+
|
|
162
|
+
<!-- prettier-ignore -->
|
|
163
|
+
```sql
|
|
164
|
+
SELECT Books.BookId,Books.Title,Books.Price,Authors.LastName FROM Books INNER JOIN Authors ON Books.AuthorId=Authors.Id WHERE Books.InStock=1 ORDER BY Books.Title ASC;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Output** (default options)
|
|
168
|
+
|
|
169
|
+
```sql
|
|
170
|
+
select
|
|
171
|
+
Books.BookId,
|
|
172
|
+
Books.Title,
|
|
173
|
+
Books.Price,
|
|
174
|
+
Authors.LastName
|
|
175
|
+
from
|
|
176
|
+
Books
|
|
177
|
+
inner join Authors on Books.AuthorId = Authors.Id
|
|
178
|
+
where Books.InStock = 1
|
|
179
|
+
order by Books.Title asc;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Documentation
|
|
183
|
+
|
|
184
|
+
- [Getting Started](docs/getting-started.md) — installation, build from source, editor setup
|
|
185
|
+
- [Options Reference](docs/options.md) — all options with before/after examples
|
|
186
|
+
- [Formatting Rules](docs/formatting.md) — how each SQL construct is formatted
|
|
187
|
+
- [Architecture](docs/architecture.md) — how the plugin works internally
|
|
188
|
+
|
|
189
|
+
## Building from Source
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
git clone <repo>
|
|
193
|
+
cd prettier-plugin-tsql
|
|
194
|
+
npm install
|
|
195
|
+
npm run build # builds .NET DLL and TypeScript
|
|
196
|
+
npm test
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
See [Getting Started](docs/getting-started.md) for full details.
|
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"runtimeTarget": {
|
|
3
|
+
"name": ".NETCoreApp,Version=v8.0",
|
|
4
|
+
"signature": ""
|
|
5
|
+
},
|
|
6
|
+
"compilationOptions": {},
|
|
7
|
+
"targets": {
|
|
8
|
+
".NETCoreApp,Version=v8.0": {
|
|
9
|
+
"SqlScriptDom/1.0.0": {
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"Microsoft.SqlServer.TransactSql.ScriptDom": "170.179.0"
|
|
12
|
+
},
|
|
13
|
+
"runtime": {
|
|
14
|
+
"SqlScriptDom.dll": {}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"Microsoft.SqlServer.TransactSql.ScriptDom/170.179.0": {
|
|
18
|
+
"runtime": {
|
|
19
|
+
"lib/net8.0/Microsoft.SqlServer.TransactSql.ScriptDom.dll": {
|
|
20
|
+
"assemblyVersion": "17.0.0.0",
|
|
21
|
+
"fileVersion": "17.0.179.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"libraries": {
|
|
28
|
+
"SqlScriptDom/1.0.0": {
|
|
29
|
+
"type": "project",
|
|
30
|
+
"serviceable": false,
|
|
31
|
+
"sha512": ""
|
|
32
|
+
},
|
|
33
|
+
"Microsoft.SqlServer.TransactSql.ScriptDom/170.179.0": {
|
|
34
|
+
"type": "package",
|
|
35
|
+
"serviceable": true,
|
|
36
|
+
"sha512": "sha512-FfxZGiMiJYLK5qWj0fqE2kEpx0QpH0wd/2FQM+k8ZleXM/3C2TiqkQJU4uGQQXLxHU7BEfL1GCPDCa3Qp7GUHA==",
|
|
37
|
+
"path": "microsoft.sqlserver.transactsql.scriptdom/170.179.0",
|
|
38
|
+
"hashPath": "microsoft.sqlserver.transactsql.scriptdom.170.179.0.nupkg.sha512"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
Binary file
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/plugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,OAAO,CAc3B,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { languages } from './language.js';
|
|
2
|
+
import { options } from './options.js';
|
|
3
|
+
import { parse, locStart, locEnd } from './parser/index.js';
|
|
4
|
+
import { printer } from './printer/index.js';
|
|
5
|
+
const plugin = {
|
|
6
|
+
languages,
|
|
7
|
+
parsers: {
|
|
8
|
+
tsql: {
|
|
9
|
+
parse,
|
|
10
|
+
astFormat: 'tsql-ast',
|
|
11
|
+
locStart,
|
|
12
|
+
locEnd,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
printers: {
|
|
16
|
+
'tsql-ast': printer,
|
|
17
|
+
},
|
|
18
|
+
options,
|
|
19
|
+
};
|
|
20
|
+
export default plugin;
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/plugin/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,MAAM,MAAM,GAAoB;IAC5B,SAAS;IACT,OAAO,EAAE;QACL,IAAI,EAAE;YACF,KAAK;YACL,SAAS,EAAE,UAAU;YACrB,QAAQ;YACR,MAAM;SACT;KACJ;IACD,QAAQ,EAAE;QACN,UAAU,EAAE,OAAO;KACtB;IACD,OAAO;CACV,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../src/plugin/language.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,eAAO,MAAM,SAAS,EAAE,eAAe,EAOtC,CAAC"}
|
package/dist/language.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language.js","sourceRoot":"","sources":["../src/plugin/language.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAsB;IACxC;QACI,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC,MAAM,CAAC;QACjB,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;QAC7B,iBAAiB,EAAE,CAAC,KAAK,CAAC;KAC7B;CACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/plugin/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAiCjD,CAAC"}
|
package/dist/options.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const options = {
|
|
2
|
+
sqlKeywordCase: {
|
|
3
|
+
type: 'choice',
|
|
4
|
+
category: 'SQL',
|
|
5
|
+
default: 'lower',
|
|
6
|
+
description: 'Casing for SQL keywords',
|
|
7
|
+
choices: [
|
|
8
|
+
{ value: 'upper', description: 'UPPERCASE keywords' },
|
|
9
|
+
{ value: 'lower', description: 'lowercase keywords' },
|
|
10
|
+
{ value: 'preserve', description: 'Preserve original casing' },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
sqlDensity: {
|
|
14
|
+
type: 'choice',
|
|
15
|
+
category: 'SQL',
|
|
16
|
+
default: 'standard',
|
|
17
|
+
description: 'Controls how spread out the formatting is',
|
|
18
|
+
choices: [
|
|
19
|
+
{ value: 'compact', description: 'Fits as much as possible on each line, wrapping at printWidth' },
|
|
20
|
+
{ value: 'standard', description: 'One clause per line; single predicates stay inline' },
|
|
21
|
+
{ value: 'spacious', description: 'Every predicate on its own line, even single ones' },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
sqlCommaStyle: {
|
|
25
|
+
type: 'choice',
|
|
26
|
+
category: 'SQL',
|
|
27
|
+
default: 'trailing',
|
|
28
|
+
description: 'Comma position in column lists (leading is not yet implemented)',
|
|
29
|
+
choices: [
|
|
30
|
+
{ value: 'trailing', description: 'Trailing comma: col1,' },
|
|
31
|
+
{ value: 'leading', description: 'Leading comma: , col1 (not yet implemented — behaves as trailing)' },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/plugin/options.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,OAAO,GAAkC;IAClD,cAAc,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,yBAAyB;QACtC,OAAO,EAAE;YACL,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE;YACrD,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE;YACrD,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,0BAA0B,EAAE;SACjE;KACa;IAClB,UAAU,EAAE;QACR,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,2CAA2C;QACxD,OAAO,EAAE;YACL,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,+DAA+D,EAAE;YAClG,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,oDAAoD,EAAE;YACxF,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,mDAAmD,EAAE;SAC1F;KACa;IAClB,aAAa,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,iEAAiE;QAC9E,OAAO,EAAE;YACL,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,uBAAuB,EAAE;YAC3D,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,mEAAmE,EAAE;SACzG;KACa;CACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/parser/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAiDxD,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAsB3C;AAuOD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE9C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE5C"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
// node-api-dotnet is a native CommonJS module; use createRequire for ESM compat.
|
|
5
|
+
let dotnetModule = null;
|
|
6
|
+
function loadDotnet() {
|
|
7
|
+
if (dotnetModule)
|
|
8
|
+
return dotnetModule;
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const dotnet = require('node-api-dotnet');
|
|
11
|
+
// Resolve DLL path relative to this file's location:
|
|
12
|
+
// compiled: dist/parser/index.js → ../../bin/dotnet
|
|
13
|
+
// source: src/plugin/parser/index.ts → ../../../bin/dotnet
|
|
14
|
+
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const isCompiled = thisDir.endsWith(path.join('dist', 'parser')) || thisDir.endsWith('dist/parser');
|
|
16
|
+
const dllPath = isCompiled
|
|
17
|
+
? path.resolve(thisDir, '../../bin/dotnet/SqlScriptDom.dll')
|
|
18
|
+
: path.resolve(thisDir, '../../../bin/dotnet/SqlScriptDom.dll');
|
|
19
|
+
// Only cache the module after a successful load.
|
|
20
|
+
try {
|
|
21
|
+
dotnet.load(dllPath);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
throw new Error(`prettier-plugin-tsql: failed to load SqlScriptDom.dll from "${dllPath}". ` +
|
|
25
|
+
`Make sure .NET 8+ is installed and the package was installed correctly. ` +
|
|
26
|
+
`Original error: ${e instanceof Error ? e.message : String(e)}`);
|
|
27
|
+
}
|
|
28
|
+
dotnetModule = dotnet;
|
|
29
|
+
return dotnetModule;
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Public parse entry point
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
export function parse(text) {
|
|
35
|
+
const { SqlParser } = loadDotnet().PrettierTsql;
|
|
36
|
+
const result = JSON.parse(SqlParser.Parse(text));
|
|
37
|
+
if (result.errors?.length) {
|
|
38
|
+
const e = result.errors[0];
|
|
39
|
+
throw new SyntaxError(`T-SQL parse error at ${e.line}:${e.column}: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
if (!result.ast) {
|
|
42
|
+
throw new Error('Parser returned no AST and no errors');
|
|
43
|
+
}
|
|
44
|
+
if (result.comments?.length) {
|
|
45
|
+
attachComments(result.ast, result.comments, text);
|
|
46
|
+
}
|
|
47
|
+
return result.ast;
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Comment attachment
|
|
51
|
+
//
|
|
52
|
+
// Comments from ScriptDom are separate from the AST. We attach them to AST
|
|
53
|
+
// nodes in three passes so the printer can emit them in the right position.
|
|
54
|
+
//
|
|
55
|
+
// Pass 1 — Trailing same-line comments (-- style):
|
|
56
|
+
// Find line comments on the same source line as a statement or INSERT row
|
|
57
|
+
// and store them as node.trailingComment.
|
|
58
|
+
//
|
|
59
|
+
// Pass 2 — Leading / pre-body comments:
|
|
60
|
+
// For each remaining comment, find the first statement that starts after
|
|
61
|
+
// the comment ends and store it as node.leadingComments[].
|
|
62
|
+
// Comments that fall physically *inside* a statement's span are routed to
|
|
63
|
+
// node.preBodyComments[] (before the body) or node.postParamComments[]
|
|
64
|
+
// (after the last parameter but before AS/BEGIN).
|
|
65
|
+
// Comments after the last statement in the file attach to the last
|
|
66
|
+
// statement's trailingComment so they are never silently dropped.
|
|
67
|
+
//
|
|
68
|
+
// Pass 3 — Intra-statement comments (commented-out predicates, etc.):
|
|
69
|
+
// Any comment still inside a statement's span is attached to the nearest
|
|
70
|
+
// surrounding AST descendant — preferring the forward neighbour as a
|
|
71
|
+
// leadingComment when that neighbour is a statement node, otherwise the
|
|
72
|
+
// backward neighbour as a trailingComment.
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
function attachComments(ast, comments, text) {
|
|
75
|
+
const used = new Set();
|
|
76
|
+
const batches = (ast.props?.['batches'] ?? []);
|
|
77
|
+
const allStatements = batches.flatMap((b) => (b.props?.['statements'] ?? []));
|
|
78
|
+
attachSameLineTrailing(batches, comments, text, used);
|
|
79
|
+
attachLeadingAndPreBody(allStatements, comments, used);
|
|
80
|
+
attachIntraStatement(batches, comments, used);
|
|
81
|
+
}
|
|
82
|
+
// Pass 1: trailing line comments on the same source line as a statement.
|
|
83
|
+
function attachSameLineTrailing(batches, comments, text, used) {
|
|
84
|
+
for (const batch of batches) {
|
|
85
|
+
const statements = (batch.props?.['statements'] ?? []);
|
|
86
|
+
for (const stmt of statements) {
|
|
87
|
+
// For INSERT … VALUES, also check each row for its own trailing comment.
|
|
88
|
+
if (stmt.type === 'InsertStatement') {
|
|
89
|
+
const source = stmt.props?.['source'];
|
|
90
|
+
if (source?.type === 'ValuesSource') {
|
|
91
|
+
for (const row of (source.props?.['rows'] ?? [])) {
|
|
92
|
+
const c = findTrailingLineComment(row, comments, text, used);
|
|
93
|
+
if (c) {
|
|
94
|
+
row.trailingComment = c.text;
|
|
95
|
+
used.add(c);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const c = findTrailingLineComment(stmt, comments, text, used);
|
|
101
|
+
if (c) {
|
|
102
|
+
stmt.trailingComment = c.text;
|
|
103
|
+
used.add(c);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Pass 2: leading comments (standalone lines before a statement) and
|
|
109
|
+
// pre-body comments (inside a statement but before its body start).
|
|
110
|
+
function attachLeadingAndPreBody(allStatements, comments, used) {
|
|
111
|
+
const unusedSorted = comments.filter((c) => !used.has(c)).sort((a, b) => a.startOffset - b.startOffset);
|
|
112
|
+
for (const c of unusedSorted) {
|
|
113
|
+
// Check whether the comment is physically inside a statement's span.
|
|
114
|
+
const container = allStatements.find((s) => c.startOffset > s.startOffset && c.endOffset <= s.endOffset);
|
|
115
|
+
if (container) {
|
|
116
|
+
// The comment is inside a statement. Route it to preBodyComments or
|
|
117
|
+
// postParamComments if it falls before the body start boundary.
|
|
118
|
+
//
|
|
119
|
+
// bodyStart (added by AstBuilder) = StatementList.StartOffset, which
|
|
120
|
+
// ScriptDom sets to the BEGIN keyword — the boundary between
|
|
121
|
+
// "header / params" and "body". For CREATE VIEW we fall back to
|
|
122
|
+
// the body node's own startOffset.
|
|
123
|
+
const bodyStartProp = container.props?.['bodyStart'];
|
|
124
|
+
const bodyProp = container.props?.['body'];
|
|
125
|
+
const bodyNode = !Array.isArray(bodyProp) &&
|
|
126
|
+
bodyProp != null &&
|
|
127
|
+
typeof bodyProp === 'object' &&
|
|
128
|
+
'startOffset' in bodyProp
|
|
129
|
+
? bodyProp
|
|
130
|
+
: undefined;
|
|
131
|
+
const beforePoint = typeof bodyStartProp === 'number' ? bodyStartProp : bodyNode?.startOffset;
|
|
132
|
+
if (beforePoint !== undefined && c.endOffset <= beforePoint) {
|
|
133
|
+
// Distinguish "before parameter list" from "after last parameter".
|
|
134
|
+
const paramsProp = container.props?.['parameters'];
|
|
135
|
+
const params = Array.isArray(paramsProp) ? paramsProp : [];
|
|
136
|
+
const lastParam = params.at(-1);
|
|
137
|
+
if (lastParam && c.startOffset > lastParam.endOffset) {
|
|
138
|
+
// e.g. /*WITH ENCRYPTION*/ after params but before AS
|
|
139
|
+
container.postParamComments = container.postParamComments ?? [];
|
|
140
|
+
container.postParamComments.push(c.text);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Banner / descriptive comment before the param list
|
|
144
|
+
container.preBodyComments = container.preBodyComments ?? [];
|
|
145
|
+
container.preBodyComments.push(c.text);
|
|
146
|
+
}
|
|
147
|
+
used.add(c);
|
|
148
|
+
}
|
|
149
|
+
// Do not re-attach this comment as a leading comment elsewhere.
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
// Not inside any statement — attach to the first statement that starts
|
|
153
|
+
// after this comment.
|
|
154
|
+
const target = allStatements.find((s) => s.startOffset >= c.endOffset);
|
|
155
|
+
if (target) {
|
|
156
|
+
target.leadingComments = target.leadingComments ?? [];
|
|
157
|
+
target.leadingComments.push(c.text);
|
|
158
|
+
used.add(c);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Comment appears after all statements (end of file).
|
|
162
|
+
// Attach to the last statement so it is never silently dropped.
|
|
163
|
+
const last = allStatements.at(-1);
|
|
164
|
+
if (last) {
|
|
165
|
+
last.trailingComment = last.trailingComment ? last.trailingComment + '\n' + c.text : c.text;
|
|
166
|
+
used.add(c);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Pass 3: intra-statement comments (e.g. commented-out WHERE predicates).
|
|
172
|
+
function attachIntraStatement(batches, comments, used) {
|
|
173
|
+
for (const batch of batches) {
|
|
174
|
+
const statements = (batch.props?.['statements'] ?? []);
|
|
175
|
+
for (const stmt of statements) {
|
|
176
|
+
const internal = comments
|
|
177
|
+
.filter((c) => !used.has(c) && c.startOffset >= stmt.startOffset && c.endOffset <= stmt.endOffset)
|
|
178
|
+
.sort((a, b) => a.startOffset - b.startOffset);
|
|
179
|
+
if (internal.length === 0)
|
|
180
|
+
continue;
|
|
181
|
+
const descendants = [];
|
|
182
|
+
collectDescendants(stmt, descendants);
|
|
183
|
+
for (const c of internal) {
|
|
184
|
+
// Find the nearest backward neighbour (highest endOffset ≤ comment start)
|
|
185
|
+
// and the nearest forward neighbour (lowest startOffset ≥ comment end).
|
|
186
|
+
let best = null;
|
|
187
|
+
let bestForward = null;
|
|
188
|
+
for (const node of descendants) {
|
|
189
|
+
if (node.endOffset <= c.startOffset) {
|
|
190
|
+
if (!best || node.endOffset >= best.endOffset)
|
|
191
|
+
best = node;
|
|
192
|
+
}
|
|
193
|
+
if (node.startOffset >= c.endOffset) {
|
|
194
|
+
if (!bestForward || node.startOffset < bestForward.startOffset)
|
|
195
|
+
bestForward = node;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Prefer attaching as a leading comment to the next statement node.
|
|
199
|
+
// printStatementWithComments will emit it before the statement body
|
|
200
|
+
// (e.g. a comment between BEGIN and the first SELECT).
|
|
201
|
+
if (bestForward?.type.endsWith('Statement')) {
|
|
202
|
+
bestForward.leadingComments = bestForward.leadingComments ?? [];
|
|
203
|
+
bestForward.leadingComments.push(c.text);
|
|
204
|
+
used.add(c);
|
|
205
|
+
}
|
|
206
|
+
else if (best) {
|
|
207
|
+
best.trailingComment = best.trailingComment ? best.trailingComment + '\n' + c.text : c.text;
|
|
208
|
+
used.add(c);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// No suitable neighbour — fall back to the containing statement.
|
|
212
|
+
stmt.leadingComments = stmt.leadingComments ?? [];
|
|
213
|
+
stmt.leadingComments.push(c.text);
|
|
214
|
+
used.add(c);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Helpers
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
/** Recursively collect all descendant nodes into result (including node itself). */
|
|
224
|
+
function collectDescendants(node, result) {
|
|
225
|
+
result.push(node);
|
|
226
|
+
const props = node.props;
|
|
227
|
+
if (!props)
|
|
228
|
+
return;
|
|
229
|
+
for (const val of Object.values(props)) {
|
|
230
|
+
if (!val)
|
|
231
|
+
continue;
|
|
232
|
+
if (Array.isArray(val)) {
|
|
233
|
+
for (const item of val) {
|
|
234
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
235
|
+
collectDescendants(item, result);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else if (typeof val === 'object' && 'type' in val) {
|
|
240
|
+
collectDescendants(val, result);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Find a line comment (`-- ...`) that starts on the same source line as node
|
|
246
|
+
* and has not already been consumed.
|
|
247
|
+
*/
|
|
248
|
+
function findTrailingLineComment(node, comments, text, used) {
|
|
249
|
+
return comments.find((c) => c.type === 'line' &&
|
|
250
|
+
!used.has(c) &&
|
|
251
|
+
c.startOffset >= node.endOffset &&
|
|
252
|
+
!text.substring(node.endOffset, c.startOffset).includes('\n'));
|
|
253
|
+
}
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
// Prettier loc helpers
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
export function locStart(node) {
|
|
258
|
+
return node.startOffset;
|
|
259
|
+
}
|
|
260
|
+
export function locEnd(node) {
|
|
261
|
+
return node.endOffset;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=index.js.map
|