introspeql 0.0.3 → 1.0.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.
Files changed (120) hide show
  1. package/README.md +286 -85
  2. package/dist/comments/comment-converter.d.ts +54 -0
  3. package/dist/comments/comment-converter.js +185 -0
  4. package/dist/comments/index.d.ts +2 -0
  5. package/dist/comments/index.js +18 -0
  6. package/dist/comments/prettify-comment.d.ts +1 -0
  7. package/dist/comments/prettify-comment.js +18 -0
  8. package/dist/config/connection-options.d.ts +14 -0
  9. package/dist/config/connection-options.js +42 -0
  10. package/dist/config/default-type-mappings.d.ts +13 -0
  11. package/dist/config/default-type-mappings.js +16 -0
  12. package/dist/config/entity-data.d.ts +6 -0
  13. package/dist/config/entity-data.js +8 -0
  14. package/dist/config/function-options.d.ts +23 -0
  15. package/dist/config/function-options.js +74 -0
  16. package/dist/config/general-options.d.ts +7 -0
  17. package/dist/config/general-options.js +75 -0
  18. package/dist/config/index.d.ts +71 -0
  19. package/dist/config/index.js +29 -0
  20. package/dist/config/output-options.d.ts +8 -0
  21. package/dist/config/output-options.js +40 -0
  22. package/dist/config/table-options.d.ts +21 -0
  23. package/dist/config/table-options.js +48 -0
  24. package/dist/enums/enum-data.d.ts +9 -0
  25. package/dist/enums/enum-data.js +11 -0
  26. package/dist/enums/enum-definition.d.ts +7 -0
  27. package/dist/enums/enum-definition.js +24 -0
  28. package/dist/enums/index.d.ts +4 -0
  29. package/dist/enums/index.js +20 -0
  30. package/dist/enums/is-enum-replaced-with-custom-type.d.ts +3 -0
  31. package/dist/enums/is-enum-replaced-with-custom-type.js +9 -0
  32. package/dist/enums/read-enum-data.d.ts +3 -0
  33. package/dist/enums/read-enum-data.js +57 -0
  34. package/dist/functions/function-data.d.ts +26 -0
  35. package/dist/functions/function-data.js +27 -0
  36. package/dist/functions/function-definition.d.ts +7 -0
  37. package/dist/functions/function-definition.js +21 -0
  38. package/dist/functions/index.d.ts +9 -0
  39. package/dist/functions/index.js +25 -0
  40. package/dist/functions/overload-type-definition.d.ts +8 -0
  41. package/dist/functions/overload-type-definition.js +21 -0
  42. package/dist/functions/parameter-type-definition-builder.d.ts +16 -0
  43. package/dist/functions/parameter-type-definition-builder.js +41 -0
  44. package/dist/functions/parameter-type-definition.d.ts +9 -0
  45. package/dist/functions/parameter-type-definition.js +28 -0
  46. package/dist/functions/read-function-data.d.ts +4 -0
  47. package/dist/functions/read-function-data.js +93 -0
  48. package/dist/functions/return-type-definition.d.ts +7 -0
  49. package/dist/functions/return-type-definition.js +20 -0
  50. package/dist/functions/should-include-function.d.ts +2 -0
  51. package/dist/functions/should-include-function.js +6 -0
  52. package/dist/functions/should-include-overload.d.ts +2 -0
  53. package/dist/functions/should-include-overload.js +23 -0
  54. package/dist/index.d.ts +3 -2
  55. package/dist/index.js +7 -1
  56. package/dist/introspeql.d.ts +7 -0
  57. package/dist/introspeql.js +161 -0
  58. package/dist/schemas/index.d.ts +3 -0
  59. package/dist/schemas/index.js +19 -0
  60. package/dist/schemas/read-schema-data.d.ts +15 -0
  61. package/dist/schemas/read-schema-data.js +182 -0
  62. package/dist/schemas/schema-definition-factory.d.ts +15 -0
  63. package/dist/schemas/schema-definition-factory.js +167 -0
  64. package/dist/schemas/schema-definition.d.ts +11 -0
  65. package/dist/schemas/schema-definition.js +38 -0
  66. package/dist/shared/convert-pg-identifier-to-ts-identifier.d.ts +19 -0
  67. package/dist/shared/convert-pg-identifier-to-ts-identifier.js +57 -0
  68. package/dist/shared/directives.d.ts +12 -0
  69. package/dist/shared/directives.js +16 -0
  70. package/dist/shared/get-tokens.d.ts +1 -0
  71. package/dist/shared/get-tokens.js +7 -0
  72. package/dist/shared/indent.d.ts +1 -0
  73. package/dist/shared/indent.js +9 -0
  74. package/dist/shared/index.d.ts +5 -0
  75. package/dist/shared/index.js +21 -0
  76. package/dist/shared/parsing-error.d.ts +3 -0
  77. package/dist/shared/parsing-error.js +26 -0
  78. package/dist/tables/column-data.d.ts +14 -0
  79. package/dist/tables/column-data.js +16 -0
  80. package/dist/tables/column-definition.d.ts +8 -0
  81. package/dist/tables/column-definition.js +19 -0
  82. package/dist/tables/column-type-definition.d.ts +7 -0
  83. package/dist/tables/column-type-definition.js +19 -0
  84. package/dist/tables/index.d.ts +8 -0
  85. package/dist/tables/index.js +24 -0
  86. package/dist/tables/read-column-data.d.ts +3 -0
  87. package/dist/tables/read-column-data.js +55 -0
  88. package/dist/tables/read-table-data.d.ts +4 -0
  89. package/dist/tables/read-table-data.js +84 -0
  90. package/dist/tables/should-include-table.d.ts +10 -0
  91. package/dist/tables/should-include-table.js +24 -0
  92. package/dist/tables/table-data.d.ts +8 -0
  93. package/dist/tables/table-data.js +10 -0
  94. package/dist/tables/table-definition.d.ts +10 -0
  95. package/dist/tables/table-definition.js +43 -0
  96. package/dist/types/index.d.ts +1 -0
  97. package/dist/types/index.js +17 -0
  98. package/dist/types/lookup-type.d.ts +15 -0
  99. package/dist/types/lookup-type.js +25 -0
  100. package/package.json +33 -18
  101. package/dist/append-schema.d.ts +0 -8
  102. package/dist/append-schema.js +0 -96
  103. package/dist/generate-types.d.ts +0 -8
  104. package/dist/generate-types.js +0 -111
  105. package/dist/introspect-columns.d.ts +0 -21
  106. package/dist/introspect-columns.js +0 -53
  107. package/dist/introspect-enum.d.ts +0 -20
  108. package/dist/introspect-enum.js +0 -24
  109. package/dist/introspect-procedures.d.ts +0 -53
  110. package/dist/introspect-procedures.js +0 -129
  111. package/dist/introspect-tables.d.ts +0 -19
  112. package/dist/introspect-tables.js +0 -43
  113. package/dist/introspeql-config.d.ts +0 -40
  114. package/dist/introspeql-config.js +0 -104
  115. package/dist/prepare-data-for-writing.d.ts +0 -38
  116. package/dist/prepare-data-for-writing.js +0 -145
  117. package/dist/snake-case-to-pascal-case.d.ts +0 -8
  118. package/dist/snake-case-to-pascal-case.js +0 -17
  119. package/dist/write-header.d.ts +0 -2
  120. package/dist/write-header.js +0 -10
package/README.md CHANGED
@@ -1,113 +1,314 @@
1
1
  # IntrospeQL
2
2
 
3
- IntrospeQL is a TypeScript utility for **introspecting PostgreSQL** schemas
4
- (tables, functions, types) and **generating TypeScript types** from them.
5
- The aim is to make capturing PostgreSQL types and producing matching TypeScript
6
- types easy, comprehensive, and accurate.
3
+ IntrospeQL reads information about the schemas, tables, columns, functions, and
4
+ enums in your PostgreSQL database and produces a TypeScript file detailing
5
+ type information for each database object.
7
6
 
8
- > ⚠️ **Warning: Alpha / Not Production Ready**
9
- > This library is currently in alpha. Use with caution; it is not yet hardened
10
- > for production.
7
+ ## Installation
11
8
 
12
- ## Features
9
+ Install IntrospeQL as a dev dependency:
13
10
 
14
- - Connects to a PostgreSQL database and introspects table schemas, functions,
15
- and enums.
16
- - Automatically emits corresponding TypeScript types.
17
- - Converts PostgreSQL types to TypeScript types in alignment with Node-Postgres
18
- defaults. Can be extended to convert PostgreSQL types to any desired TypeScript
19
- type.
11
+ ```
12
+ npm install --save-dev introspeql
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ Install [tsx](https://tsx.is/) and [@types/node](https://www.npmjs.com/package/@types/node):
18
+
19
+ ```
20
+ npm install --save-dev tsx @types/node
21
+ ```
22
+
23
+ Install [dotenv](https://www.npmjs.com/package/dotenv):
24
+
25
+ ```
26
+ npm install --save dotenv
27
+ ```
20
28
 
21
- ## Example Usage
29
+ Add the following entry to the scripts object in package.json:
22
30
 
23
- ```typescript
24
- // src/scripts/gen-db-types.ts
31
+ ```
32
+ "gen-types": "npx tsx scripts/gen-types.ts"
33
+ ```
34
+
35
+ Create a scripts directory at the root level of your project, and inside this
36
+ folder, create a new TypeScript file called `gen-types.ts`.
37
+
38
+ Add the following content to `scripts/gen-types.ts`:
39
+
40
+ ```
25
41
  import 'dotenv/config';
26
42
  import path from 'node:path';
27
- import { generateTypes, type IntrospeQLConfig } from 'introspeql';
43
+ import { introspeql, type IntrospeQLConfig } from 'introspeql';
44
+
45
+ const outFile = '/generated/type-definitions.ts';
28
46
 
29
47
  const config: IntrospeQLConfig = {
30
- dbConnectionString: process.env.DATABASE_URL,
31
- outFile: path.join(import.meta.dirname, '../db/types.ts'),
32
- schemas: ['public'],
33
- ignoreTables: [
34
- {
35
- schema: 'public',
36
- name: 'spatial_ref_sys',
37
- },
38
- ],
39
- header:
40
- `
41
- // Custom header:
42
- import type { Geography } from '../model/geography';
43
- `
44
- types: {
45
- 'public.geography': 'Geography'
48
+ schemas: ['public'], // Add other schemas as necessary
49
+ dbConnectionParams: {
50
+ host: process.env.DB_HOST,
51
+ port: +process.env.DB_PORT!,
52
+ database: process.env.DB_NAME,
53
+ user: process.env.DB_USER,
54
+ password: process.env.DB_PASSWORD,
46
55
  },
56
+ writeToDisk: true,
57
+ outFile: path.join(__dirname, '..' + outFile),
58
+ header:
59
+ '/* This file has been generated by IntrospeQL and should not be edited directly. */',
60
+ types: {
61
+ 'pg_catalog.void': 'void'
62
+ }
47
63
  };
48
64
 
49
- generateTypes(config);
65
+ genTypes();
66
+
67
+ async function genTypes() {
68
+ await introspeql(config);
69
+ console.log("Type definition file created at " + outFile);
70
+ }
71
+ ```
72
+
73
+ Create a `.gitignore` file at the root of your project and make sure it includes
74
+ `node_modules` and `.env`:
75
+
76
+ ```
77
+ # .gitignore
78
+ node_modules/
79
+ .env
50
80
  ```
51
81
 
52
- ## Example Output
82
+ Create a `.env` file at the root of your project and add your database connection
83
+ information:
84
+ ```
85
+ # .env
86
+ DB_HOST= # Add your host here, often just localhost
87
+ DB_PORT= # Add your port here
88
+ DB_NAME= # Add the name of the database you wish to connect to here
89
+ DB_USER= # Add the PostgreSQL username that should be used to connect to the db
90
+ DB_PASSWORD= # Add the password for this user here
91
+ ```
53
92
 
54
- ```typescript
55
- // src/db/types.ts
93
+ Execute your script:
56
94
 
57
- /* This file was generated by IntrospeQL. Do not edit manually. */
58
- // Custom header:
59
- import type { Geography } from '../model/geography';
95
+ ```
96
+ npm run gen-types
97
+ ```
60
98
 
61
- export namespace Public {
62
- export const SchemaName = 'public';
99
+ A type definition file will have been generated at `generated/type-definitions.ts`.
63
100
 
64
- export namespace Enums {
65
- export enum DistanceUnits {
66
- METERS = 'METERS',
67
- KILOMETERS = 'KILOMETERS',
68
- MILES = 'MILES',
69
- }
70
- }
101
+ > ⚠️ **Important**
102
+ > Never hard code sensitive information directly into your project. Instead,
103
+ > load such information from environment variables and ensure that .env files
104
+ > are not committed to source control.
71
105
 
72
- export namespace Tables {
73
- export namespace StoreLocations {
74
- export const TableName = 'store_locations';
75
-
76
- export enum ColumnNames {
77
- Id = 'id',
78
- Coordinates = 'coordinates',
79
- StreetAddressLine1 = 'street_address_line_1',
80
- StreetAddressLine2 = 'street_address_line_2',
81
- City = 'city',
82
- State = 'state',
83
- ZipCode = 'zip_code',
84
- }
85
-
86
- export interface RowType {
87
- [ColumnNames.Id]: number;
88
- [ColumnNames.Coordinates]: Geography;
89
- [ColumnNames.StreetAddressLine1]: string;
90
- [ColumnNames.StreetAddressLine2]: string | null;
91
- [ColumnNames.City]: string;
92
- [ColumnNames.State]: string;
93
- [ColumnNames.ZipCode]: string;
94
- }
95
- }
96
- }
106
+ ## Output
97
107
 
98
- export namespace Procedures {
99
- export namespace DistanceWithUnits {
100
- export const ProcedureName = 'distance_with_units';
108
+ The type definitions file consists of nested
109
+ [namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html).
110
+ Each top-level namespace represents a schema and contains the name of the schema
111
+ as well as type definitions for its tables, functions, and enums, grouped by
112
+ category into nested namespaces. Each table and function, in turn, consists of a
113
+ namespace.
101
114
 
102
- export const ArgNames = ['point_a', 'point_b', 'units'] as const;
115
+ Table namespaces contain the name of the table, a union of string literals
116
+ representing the names of the columns of the table, and an object-type
117
+ representing the structure of each row.
103
118
 
104
- export type ArgTypes = [Geography, Geography, Enums.DistanceUnits];
119
+ Function namespaces contain the name of the function and an array of overloads.
120
+ Each overload contains an array of parameter types and (separately from the
121
+ array of parameter types) the return type.
122
+
123
+ Enums are represented as unions of string literals, with each string literal
124
+ representing one possible value of the enum.
125
+
126
+ A header can be added to the file. This is useful to import types that will be
127
+ used later in the file. In the example, we simply added a comment to the top of
128
+ the file indicating that the file was autogenerated and should not be changed
129
+ manually.
130
+
131
+ ## Configuration
132
+
133
+ ### Root-Level Configuration
134
+
135
+ The following options exist directly on the top-level configuration object.
136
+
137
+ | Option | Type | Required | Default | Description |
138
+ | -------------------- | ------------------------ | ----------- | ------------ | ----------------------------------------------------------------------------------------------- |
139
+ | `schemas` | `string[]` | No | `['public']` | PostgreSQL schemas to introspect. At least one schema must be specified. |
140
+ | `header` | `string` | No | — | Text inserted at the top of the generated output file (e.g., additional type imports or definitions). |
141
+ | `types` | `Record<string, string>` | No | — | Custom PostgreSQL-to-TypeScript type mappings that override built-in defaults. These should be specified in the format `<schema>.<type>`, e.g. `'pg_catalog.int8'` |
142
+ | `copyComments` | `boolean` | No | `true` | Whether database comments are copied into generated TypeScript documentation comments. |
143
+ | `dbConnectionString` | `string` | Conditional | — | PostgreSQL connection string. Exactly one of this or `dbConnectionParams` must be provided. |
144
+ | `dbConnectionParams` | `object` | Conditional | — | PostgreSQL connection parameters. Exactly one of this or `dbConnectionString` must be provided. |
145
+ | `writeToDisk` | `boolean` | Yes | — | Controls whether generated output is written to disk. |
146
+ | `outFile` | `string` | Conditional | — | Output file path. Required when `writeToDisk` is `true`. |
147
+ | `tables` | `TableOptions` | No | — | Table filtering configuration. |
148
+ | `functions` | `FunctionOptions` | No | — | Function filtering and typing configuration. |
149
+
150
+ #### `dbConnectionParams`
151
+
152
+ | Option | Type | Required | Description |
153
+ | ---------- | -------- | -------- | ------------------- |
154
+ | `user` | `string` | No | Database user name. |
155
+ | `password` | `string` | No | Database password. |
156
+ | `host` | `string` | No | Database host. |
157
+ | `port` | `number` | No | Database port. |
158
+ | `database` | `string` | No | Database name. |
159
+
160
+ ---
161
+
162
+ ### Table Options
163
+
164
+ Controls which database tables are included in the generated output.
165
+
166
+ | Option | Type | Required | Default | Description |
167
+ | --------------- | ---------------------------- | -------- | ------------- | --------------------------------------------------- |
168
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how table filtering is applied. |
169
+ | `excludeTables` | `string[]` | No | `[]` | Tables to exclude when operating in inclusive mode. |
170
+ | `includeTables` | `string[]` | No | `[]` | Tables to include when operating in exclusive mode. |
171
+
172
+ ---
173
+
174
+ ### Function Options
175
+
176
+ Controls which PostgreSQL functions are included and how their types are generated.
177
+
178
+ | Option | Type | Required | Default | Description |
179
+ | --------------------- | ---------------------------- | -------- | ------------- | ----------------------------------------------------------- |
180
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how function filtering is applied. |
181
+ | `excludeFunctions` | `string[]` | No | `[]` | Functions to exclude when operating in inclusive mode. |
182
+ | `includeFunctions` | `string[]` | No | `[]` | Functions to include when operating in exclusive mode. |
183
+ | `nullableArgs` | `boolean` | No | `false` | Treat function arguments as nullable in generated types. |
184
+ | `nullableReturnTypes` | `boolean` | No | `true` | Treat function return types as nullable in generated types. |
185
+
186
+ ## Types
187
+
188
+ Default type mappings are based on the types produced by
189
+ [node-postgres](https://node-postgres.com/). These defaults can be added to or
190
+ overridden using configuration options described above. Type definitions for
191
+ enums will be generated automatically, provided that the enum is used somewhere
192
+ in a table or function definition.
193
+
194
+ To define custom types, use the header to import or define them and use the
195
+ `types` configuration option to map PostgreSQL types to your custom types.
196
+
197
+ Multi-dimensional-array-type columns are recognized. Columns to which a `NOT NULL`
198
+ constraint has been applied will be non-nullable. Optional and variadic function
199
+ parameters are recognized.
200
+
201
+ By default, node-postgres will transform the return value of a function that
202
+ returns void into an empty string. Therefore, by default, the generated
203
+ TypeScript return type of PostgreSQL functions that return void will be
204
+ `string`. If desired, this can be overridden using the `types` configuration
205
+ option.
206
+
207
+ The default value for unrecognized types is `string`.
208
+
209
+ ## Functions
210
+
211
+ Only functions whose `prokind` is `'f'` in `pg_catalog.pg_proc` are recognized
212
+ (i.e. normal functions, not procedures or aggregate functions).
213
+
214
+ Function overloads are recognized.
215
+
216
+ ## Directives
217
+
218
+ IntrospeQL supports the inclusion of several directives which, when included in
219
+ a PostgreSQL comment, can modify how IntrospeQL interacts with the database
220
+ object to which that comment is applied.
221
+
222
+ Directives are case-insensitive but must be separated from each other and from
223
+ other text by a whitespace character (a space, newline, etc).
224
+
225
+ Here is an example of adding a directive to the `config_key` column of a table
226
+ called `config` in the `reporting` schema:
227
+
228
+ ```
229
+ COMMENT ON COLUMN reporting.config.config_key IS
230
+ '@introspeql-enable-tsdoc-comments
231
+ Unique configuration key.';
232
+ ```
233
+
234
+ Below, please find a table of all valid directives:
235
+
236
+ | Category | Directive | Effect |
237
+ | -------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
238
+ | Objects | @introspeql-exclude | Excludes a table or function overload when mode is `'inclusive'` |
239
+ | Objects | @introspeql-include | Includes a table or function overload when mode is `'exclusive'` |
240
+ | Types | @introspeql-enable-nullable-args | Makes each parameter of the function overload to which it is applied nullable when `nullableArgs` is `false` in `config.functions` |
241
+ | Types | @introspeql-disable-nullable-args | Makes each parameter of the function overload to which it is applied non-nullable when `nullableArgs` is `true` in `config.functions` |
242
+ | Types | @introspeql-enable-nullable-return-types | Makes the return type of the function overload to which it is applied nullable when `nullableReturnTypes` is `false` in `config.functions` |
243
+ | Types | @introspeql-disable-nullable-return-types | Makes the return type of the function overload to which it is applied non-nullable when `nullableReturnTypes` is `true` in `config.functions` |
244
+ | Comments | @introspeql-enable-tsdoc-comments | Copies the comments of the table, column, or enum to which it is applied even when `config.copyComments` is false or is an array that does not include the given database object type. |
245
+ | Comments | @introspeql-disable-tsdoc-comments | Ignores the comments of the table, column, or enum to which it is applied even when `config.copyComments` is true or is an array that includes the given database object type. |
246
+ | Comments | @introspeql-begin-tsdoc-comment | Copies a portion of a PostgreSQL comment into the generated type definition file beginning after this directive and ending at the next `@introspeql-end-tsdoc-comment` directive or at the end of the comment. |
247
+ | Comments | @introspeql-end-tsdoc-comment | Omits a portion of a PostgreSQL comment from the generated type definition file beginning after this directive and ending at the next `@introspeql-begin-tsdoc-comment` directive or at the end of the comment. |
248
+
249
+ The package also exports a TypeScript enum of all valid directives:
250
+
251
+ ```
252
+ import { Directives } from 'introspeql';
253
+ ```
254
+
255
+ ## Comments
256
+
257
+ [Comments](https://www.postgresql.org/docs/current/sql-comment.html) can be
258
+ applied to PostgreSQL database objects with the following syntax:
105
259
 
106
- export type ReturnType = number;
107
- }
108
- }
109
- }
110
260
  ```
261
+ COMMENT ON COLUMN auth.users.user_id IS
262
+ 'Unique identifier for the user.';
263
+ ```
264
+
265
+ If IntrospeQL is configured to copy the comments for a given table, enum, or
266
+ column, PostgreSQL comments applied to that database object will be copied into
267
+ the type definition file as [TSDoc](https://tsdoc.org/) comments.
268
+
269
+ Note that all `@introspeql-` directives will always be removed when the copied
270
+ comment is formatted.
271
+
272
+ Comments cannot contain `*/` or a combination of
273
+ characters that results in `*/` after directives are removed. Such comments
274
+ will trigger a warning and will not be included in the output.
275
+
276
+ Assuming comment copying is enabled for a particular database object, you can
277
+ use the `@introspeql-begin-tsdoc-comment` and `@introspeql-end-tsdoc-comment`
278
+ directives to copy only certain excerpts of the comment:
279
+
280
+ ### No Directive
281
+
282
+ If neither `@introspeql-begin-tsdoc-comment` nor `@introspeql-end-tsdoc-comment`
283
+ is included, the entire comment will be copied.
284
+
285
+ ### Using Only `@introspeql-begin-tsdoc-comment`
286
+
287
+ If only `@introspeql-begin-tsdoc-comment` is included, everything before the
288
+ directive will be excluded, and everything after it will be included.
289
+
290
+ ### Using Only `@introspeql-end-tsdoc-comment`
291
+
292
+ If only `@introspeql-end-tsdoc-comment` is included, everything before the
293
+ directive will be included, and everything after it will be excluded.
294
+
295
+ ### Using Both Directives
296
+
297
+ You can alternate `@introspeql-begin-tsdoc-comment` and
298
+ `@introspeql-end-tsdoc-comment`.
299
+
300
+ You can begin or end with either directive, but you cannot include multiple
301
+ instances of the same directive in a row within the same comment (in this case,
302
+ a warning will be printed to the console and the comment will
303
+ <strong>not</strong> be copied).
304
+
305
+ When alternating between directives, content before
306
+ `@introspeql-end-tsdoc-comment` or after `@introspeql-begin-tsdoc-comment` will
307
+ be included and all other content will be excluded.
308
+
309
+ ## Contributing
310
+
311
+ If you notice a bug or would like to request a feature, please submit an issue.
111
312
 
112
313
  ## License
113
314
 
@@ -131,4 +332,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
131
332
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
132
333
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
133
334
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
134
- SOFTWARE.
335
+ SOFTWARE.
@@ -0,0 +1,54 @@
1
+ export declare class CommentConverter {
2
+ private static chunkExtractionDirectivesPattern;
3
+ private static chunkSanitizationDirectivesPattern;
4
+ /**
5
+ * Converts a PostgreSQL comment into TSDoc format, removing IntrospeQL
6
+ * directives, and formatting, prettifying, and validating the result.
7
+ */
8
+ static convertComment(pgComment: string): string;
9
+ /**
10
+ * Extracts the portions of a comment that have been flagged for inclusion
11
+ * with the `@introspeql-begin-tsdoc-comment` and/or
12
+ * `@introspeql-end-tsdoc-comment` directives, or the whole comment if no
13
+ * such directives are present.
14
+ */
15
+ private static extractChunks;
16
+ private static findNextChunkExtractionDirective;
17
+ private static extractChunk;
18
+ /**
19
+ * Extracts a directive from a comment at the provided index, regardless of
20
+ * the case of the directive as it appears in the comment.
21
+ */
22
+ private static extractDirective;
23
+ private static isEmptyChunk;
24
+ private static discardPreviousChunk;
25
+ /**
26
+ * Removes directives and leading/trailing new lines from each chunk. Filters
27
+ * out lines/chunks whose only content was directives.
28
+ */
29
+ private static sanitizeChunks;
30
+ /**
31
+ * All chunks are separated with empty lines by default, so leading and
32
+ * trailing new lines are removed from each chunk.
33
+ */
34
+ private static removeLeadingAndTrailingNewLines;
35
+ /**
36
+ * Removes all IntrospeQL directives from the provided chunks and filters
37
+ * out lines/chunks whose only contents were such directives.
38
+ */
39
+ private static removeDirectives;
40
+ /**
41
+ * Merges chunks and applies basic TSDoc formatting.
42
+ */
43
+ private static formatAndMergeChunks;
44
+ /**
45
+ * Prettifies a comment that has already received basic TSDoc formatting.
46
+ */
47
+ private static prettifyComment;
48
+ /**
49
+ * Verifies that *\/ occurs only at the very end of a TSDoc comment. This
50
+ * validation must occur after the TSDoc comment is produced to guarantee that
51
+ * the collapse of whitespace and/or removal of directives hasn't produced *\/.
52
+ */
53
+ private static validateComment;
54
+ }
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommentConverter = void 0;
4
+ var shared_1 = require("../shared");
5
+ var prettify_comment_1 = require("./prettify-comment");
6
+ var CommentConverter = /** @class */ (function () {
7
+ function CommentConverter() {
8
+ }
9
+ /**
10
+ * Converts a PostgreSQL comment into TSDoc format, removing IntrospeQL
11
+ * directives, and formatting, prettifying, and validating the result.
12
+ */
13
+ CommentConverter.convertComment = function (pgComment) {
14
+ // Break the comment into chunks and sanitize them.
15
+ var chunks = this.extractChunks(pgComment);
16
+ var sanitizedChunks = this.sanitizeChunks(chunks);
17
+ // Convert the comment and validate it.
18
+ var convertedComment = this.formatAndMergeChunks(sanitizedChunks);
19
+ this.validateComment(convertedComment);
20
+ // Prettify the comment and validate it.
21
+ var prettifiedComment = this.prettifyComment(convertedComment);
22
+ this.validateComment(prettifiedComment);
23
+ return prettifiedComment;
24
+ };
25
+ /**
26
+ * Extracts the portions of a comment that have been flagged for inclusion
27
+ * with the `@introspeql-begin-tsdoc-comment` and/or
28
+ * `@introspeql-end-tsdoc-comment` directives, or the whole comment if no
29
+ * such directives are present.
30
+ */
31
+ CommentConverter.extractChunks = function (pgComment) {
32
+ var chunks = [];
33
+ var previousDirective = null;
34
+ var indexOfDirective = this.findNextChunkExtractionDirective(pgComment);
35
+ var chunk = this.extractChunk(pgComment, indexOfDirective);
36
+ while (indexOfDirective >= 0) {
37
+ var directive = this.extractDirective(pgComment, indexOfDirective);
38
+ if (directive === previousDirective) {
39
+ throw new shared_1.ParsingError('Ambiguous directives: cannot have two instances of ' +
40
+ directive +
41
+ 'in a row in the same comment.');
42
+ }
43
+ else if (directive === shared_1.Directives.EndTSDocComment &&
44
+ !this.isEmptyChunk(chunk)) {
45
+ chunks.push(chunk);
46
+ }
47
+ pgComment = this.discardPreviousChunk(pgComment);
48
+ indexOfDirective = this.findNextChunkExtractionDirective(pgComment);
49
+ chunk = this.extractChunk(pgComment, indexOfDirective);
50
+ previousDirective = directive;
51
+ }
52
+ if ((!previousDirective ||
53
+ previousDirective === shared_1.Directives.BeginTSDocComment) &&
54
+ !this.isEmptyChunk(chunk)) {
55
+ chunks.push(chunk);
56
+ }
57
+ return chunks;
58
+ };
59
+ CommentConverter.findNextChunkExtractionDirective = function (pgComment) {
60
+ return pgComment.search(this.chunkExtractionDirectivesPattern);
61
+ };
62
+ CommentConverter.extractChunk = function (pgComment, indexOfDirective) {
63
+ return pgComment.slice(0, indexOfDirective >= 0 ? indexOfDirective : pgComment.length);
64
+ };
65
+ /**
66
+ * Extracts a directive from a comment at the provided index, regardless of
67
+ * the case of the directive as it appears in the comment.
68
+ */
69
+ CommentConverter.extractDirective = function (pgComment, indexOfDirective) {
70
+ /*
71
+ Add 2 to the length of the longer directive because the pattern includes
72
+ up to 1 leading and 1 trailing whitespace character.
73
+ */
74
+ var maxSearchLength = Math.max(shared_1.Directives.BeginTSDocComment.length, shared_1.Directives.EndTSDocComment.length) + 2;
75
+ var searchString = pgComment
76
+ .slice(indexOfDirective, indexOfDirective + maxSearchLength)
77
+ .trim()
78
+ .toLowerCase();
79
+ if (searchString.startsWith(shared_1.Directives.BeginTSDocComment.toLowerCase())) {
80
+ return shared_1.Directives.BeginTSDocComment;
81
+ }
82
+ else if (searchString.startsWith(shared_1.Directives.EndTSDocComment.toLowerCase())) {
83
+ return shared_1.Directives.EndTSDocComment;
84
+ }
85
+ throw new shared_1.ParsingError('No directive at the provided index.');
86
+ };
87
+ CommentConverter.isEmptyChunk = function (chunk) {
88
+ return !chunk.trim();
89
+ };
90
+ CommentConverter.discardPreviousChunk = function (pgComment) {
91
+ var _b = pgComment.match(this.chunkExtractionDirectivesPattern), index = _b.index, length = _b[0].length;
92
+ return pgComment.slice(index + length);
93
+ };
94
+ /**
95
+ * Removes directives and leading/trailing new lines from each chunk. Filters
96
+ * out lines/chunks whose only content was directives.
97
+ */
98
+ CommentConverter.sanitizeChunks = function (chunks) {
99
+ var sanitizedChunks = this.removeLeadingAndTrailingNewLines(chunks);
100
+ sanitizedChunks = this.removeDirectives(sanitizedChunks);
101
+ return sanitizedChunks;
102
+ };
103
+ /**
104
+ * All chunks are separated with empty lines by default, so leading and
105
+ * trailing new lines are removed from each chunk.
106
+ */
107
+ CommentConverter.removeLeadingAndTrailingNewLines = function (chunks) {
108
+ return chunks.map(function (chunk) {
109
+ while (chunk.startsWith('\n')) {
110
+ chunk = chunk.slice(1);
111
+ }
112
+ while (chunk.endsWith('\n')) {
113
+ chunk = chunk.slice(0, chunk.length - 1);
114
+ }
115
+ return chunk;
116
+ });
117
+ };
118
+ /**
119
+ * Removes all IntrospeQL directives from the provided chunks and filters
120
+ * out lines/chunks whose only contents were such directives.
121
+ */
122
+ CommentConverter.removeDirectives = function (chunks) {
123
+ var _this = this;
124
+ var result = [];
125
+ chunks.forEach(function (chunk) {
126
+ var lines = chunk.split('\n');
127
+ var sanitizedLines = [];
128
+ lines.forEach(function (line) {
129
+ var sanitizedLine = line.replaceAll(_this.chunkSanitizationDirectivesPattern, function (match) {
130
+ if (/\s\S+\s/.test(match)) {
131
+ return ' ';
132
+ }
133
+ return '';
134
+ });
135
+ var directiveRemoved = sanitizedLine != line;
136
+ if (!directiveRemoved || sanitizedLine.trim() != '') {
137
+ sanitizedLines.push(sanitizedLine);
138
+ }
139
+ });
140
+ if (sanitizedLines.length) {
141
+ var sanitizedChunk = sanitizedLines.join('\n');
142
+ result.push(sanitizedChunk);
143
+ }
144
+ });
145
+ return result;
146
+ };
147
+ /**
148
+ * Merges chunks and applies basic TSDoc formatting.
149
+ */
150
+ CommentConverter.formatAndMergeChunks = function (chunks) {
151
+ var mergedChunks = chunks
152
+ .map(function (chunk) {
153
+ var lines = chunk.split('\n');
154
+ return lines.map(function (line) { return " * ".concat(line); }).join('\n');
155
+ })
156
+ .join('\n *\n');
157
+ return mergedChunks.length ? '/**\n' + mergedChunks + '\n */' : '';
158
+ };
159
+ /**
160
+ * Prettifies a comment that has already received basic TSDoc formatting.
161
+ */
162
+ CommentConverter.prettifyComment = function (comment) {
163
+ var prettified = (0, prettify_comment_1.prettifyComment)(comment);
164
+ return prettified;
165
+ };
166
+ /**
167
+ * Verifies that *\/ occurs only at the very end of a TSDoc comment. This
168
+ * validation must occur after the TSDoc comment is produced to guarantee that
169
+ * the collapse of whitespace and/or removal of directives hasn't produced *\/.
170
+ */
171
+ CommentConverter.validateComment = function (comment) {
172
+ if (comment !== '' && comment.indexOf('*/') !== comment.length - 2) {
173
+ throw new shared_1.ParsingError('Comments cannot contain */');
174
+ }
175
+ };
176
+ var _a;
177
+ _a = CommentConverter;
178
+ CommentConverter.chunkExtractionDirectivesPattern = new RegExp("(^|\\s)(".concat(shared_1.Directives.BeginTSDocComment, "|").concat(shared_1.Directives.EndTSDocComment, ")($|\\s)"), 'i');
179
+ (function () {
180
+ var directivesPattern = "(".concat(Object.values(shared_1.Directives).join('|'), ")");
181
+ _a.chunkSanitizationDirectivesPattern = new RegExp("(^|\\s)(".concat(directivesPattern, ")(\\s+").concat(directivesPattern, ")*($|\\s)"), 'gi');
182
+ })();
183
+ return CommentConverter;
184
+ }());
185
+ exports.CommentConverter = CommentConverter;
@@ -0,0 +1,2 @@
1
+ export * from './comment-converter';
2
+ export * from './prettify-comment';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./comment-converter"), exports);
18
+ __exportStar(require("./prettify-comment"), exports);
@@ -0,0 +1 @@
1
+ export declare function prettifyComment(comment: string): string;