introspeql 0.0.4 → 1.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.
Files changed (134) hide show
  1. package/README.md +313 -83
  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/create-relation-options.d.ts +22 -0
  11. package/dist/config/create-relation-options.js +60 -0
  12. package/dist/config/default-type-mappings.d.ts +13 -0
  13. package/dist/config/default-type-mappings.js +16 -0
  14. package/dist/config/entity-data.d.ts +6 -0
  15. package/dist/config/entity-data.js +8 -0
  16. package/dist/config/function-options.d.ts +23 -0
  17. package/dist/config/function-options.js +74 -0
  18. package/dist/config/general-options.d.ts +7 -0
  19. package/dist/config/general-options.js +75 -0
  20. package/dist/config/index.d.ts +123 -0
  21. package/dist/config/index.js +43 -0
  22. package/dist/config/materialized-view-options.d.ts +24 -0
  23. package/dist/config/materialized-view-options.js +5 -0
  24. package/dist/config/output-options.d.ts +8 -0
  25. package/dist/config/output-options.js +40 -0
  26. package/dist/config/table-options.d.ts +24 -0
  27. package/dist/config/table-options.js +5 -0
  28. package/dist/config/view-options.d.ts +24 -0
  29. package/dist/config/view-options.js +5 -0
  30. package/dist/enums/enum-data.d.ts +9 -0
  31. package/dist/enums/enum-data.js +11 -0
  32. package/dist/enums/enum-definition.d.ts +7 -0
  33. package/dist/enums/enum-definition.js +24 -0
  34. package/dist/enums/index.d.ts +4 -0
  35. package/dist/enums/index.js +20 -0
  36. package/dist/enums/is-enum-replaced-with-custom-type.d.ts +3 -0
  37. package/dist/enums/is-enum-replaced-with-custom-type.js +9 -0
  38. package/dist/enums/read-enum-data.d.ts +3 -0
  39. package/dist/enums/read-enum-data.js +57 -0
  40. package/dist/functions/function-data.d.ts +26 -0
  41. package/dist/functions/function-data.js +27 -0
  42. package/dist/functions/function-definition.d.ts +7 -0
  43. package/dist/functions/function-definition.js +21 -0
  44. package/dist/functions/index.d.ts +9 -0
  45. package/dist/functions/index.js +25 -0
  46. package/dist/functions/overload-type-definition.d.ts +8 -0
  47. package/dist/functions/overload-type-definition.js +21 -0
  48. package/dist/functions/parameter-type-definition-builder.d.ts +16 -0
  49. package/dist/functions/parameter-type-definition-builder.js +41 -0
  50. package/dist/functions/parameter-type-definition.d.ts +9 -0
  51. package/dist/functions/parameter-type-definition.js +28 -0
  52. package/dist/functions/read-function-data.d.ts +4 -0
  53. package/dist/functions/read-function-data.js +93 -0
  54. package/dist/functions/return-type-definition.d.ts +7 -0
  55. package/dist/functions/return-type-definition.js +20 -0
  56. package/dist/functions/should-include-function.d.ts +2 -0
  57. package/dist/functions/should-include-function.js +6 -0
  58. package/dist/functions/should-include-overload.d.ts +2 -0
  59. package/dist/functions/should-include-overload.js +23 -0
  60. package/dist/index.d.ts +3 -2
  61. package/dist/index.js +7 -1
  62. package/dist/introspeql.d.ts +7 -0
  63. package/dist/introspeql.js +161 -0
  64. package/dist/relations/abstract-relation-definition.d.ts +11 -0
  65. package/dist/relations/abstract-relation-definition.js +43 -0
  66. package/dist/relations/column-data.d.ts +14 -0
  67. package/dist/relations/column-data.js +16 -0
  68. package/dist/relations/column-definition.d.ts +8 -0
  69. package/dist/relations/column-definition.js +19 -0
  70. package/dist/relations/column-type-definition.d.ts +7 -0
  71. package/dist/relations/column-type-definition.js +19 -0
  72. package/dist/relations/index.d.ts +11 -0
  73. package/dist/relations/index.js +27 -0
  74. package/dist/relations/materialized-view-definition.d.ts +4 -0
  75. package/dist/relations/materialized-view-definition.js +29 -0
  76. package/dist/relations/read-column-data.d.ts +3 -0
  77. package/dist/relations/read-column-data.js +55 -0
  78. package/dist/relations/read-relation-data.d.ts +4 -0
  79. package/dist/relations/read-relation-data.js +87 -0
  80. package/dist/relations/relation-data.d.ts +8 -0
  81. package/dist/relations/relation-data.js +10 -0
  82. package/dist/relations/should-include-relation.d.ts +10 -0
  83. package/dist/relations/should-include-relation.js +24 -0
  84. package/dist/relations/table-definition.d.ts +4 -0
  85. package/dist/relations/table-definition.js +29 -0
  86. package/dist/relations/view-definition.d.ts +4 -0
  87. package/dist/relations/view-definition.js +29 -0
  88. package/dist/schemas/index.d.ts +3 -0
  89. package/dist/schemas/index.js +19 -0
  90. package/dist/schemas/read-schema-data.d.ts +17 -0
  91. package/dist/schemas/read-schema-data.js +219 -0
  92. package/dist/schemas/schema-definition-factory.d.ts +15 -0
  93. package/dist/schemas/schema-definition-factory.js +177 -0
  94. package/dist/schemas/schema-definition.d.ts +13 -0
  95. package/dist/schemas/schema-definition.js +52 -0
  96. package/dist/shared/capitalize.d.ts +1 -0
  97. package/dist/shared/capitalize.js +6 -0
  98. package/dist/shared/convert-pg-identifier-to-ts-identifier.d.ts +19 -0
  99. package/dist/shared/convert-pg-identifier-to-ts-identifier.js +55 -0
  100. package/dist/shared/directives.d.ts +12 -0
  101. package/dist/shared/directives.js +16 -0
  102. package/dist/shared/get-tokens.d.ts +1 -0
  103. package/dist/shared/get-tokens.js +7 -0
  104. package/dist/shared/indent.d.ts +1 -0
  105. package/dist/shared/indent.js +9 -0
  106. package/dist/shared/index.d.ts +6 -0
  107. package/dist/shared/index.js +22 -0
  108. package/dist/shared/parsing-error.d.ts +3 -0
  109. package/dist/shared/parsing-error.js +26 -0
  110. package/dist/types/index.d.ts +1 -0
  111. package/dist/types/index.js +17 -0
  112. package/dist/types/lookup-type.d.ts +15 -0
  113. package/dist/types/lookup-type.js +25 -0
  114. package/package.json +32 -17
  115. package/dist/append-schema.d.ts +0 -8
  116. package/dist/append-schema.js +0 -96
  117. package/dist/generate-types.d.ts +0 -8
  118. package/dist/generate-types.js +0 -110
  119. package/dist/introspect-columns.d.ts +0 -21
  120. package/dist/introspect-columns.js +0 -53
  121. package/dist/introspect-enum.d.ts +0 -20
  122. package/dist/introspect-enum.js +0 -24
  123. package/dist/introspect-procedures.d.ts +0 -53
  124. package/dist/introspect-procedures.js +0 -129
  125. package/dist/introspect-tables.d.ts +0 -19
  126. package/dist/introspect-tables.js +0 -43
  127. package/dist/introspeql-config.d.ts +0 -40
  128. package/dist/introspeql-config.js +0 -104
  129. package/dist/prepare-data-for-writing.d.ts +0 -38
  130. package/dist/prepare-data-for-writing.js +0 -145
  131. package/dist/snake-case-to-pascal-case.d.ts +0 -8
  132. package/dist/snake-case-to-pascal-case.js +0 -17
  133. package/dist/write-header.d.ts +0 -2
  134. package/dist/write-header.js +0 -10
package/README.md CHANGED
@@ -1,114 +1,344 @@
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, views, materialized
4
+ views, columns, functions, and enums in your PostgreSQL database and produces a
5
+ TypeScript file detailing 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
+ ```
28
+
29
+ Add the following entry to the scripts object in package.json:
30
+
31
+ ```
32
+ "gen-types": "npx tsx scripts/gen-types.ts"
33
+ ```
20
34
 
21
- ## Example Usage
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`.
22
37
 
23
- ```typescript
24
- // src/scripts/gen-db-types.ts
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
- ],
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,
55
+ },
56
+ writeToDisk: true,
57
+ outFile: path.join(__dirname, '..' + outFile),
39
58
  header:
40
- `
41
- // Custom header:
42
- import type { Geography } from '../model/geography';
43
- `
59
+ '/* This file has been generated by IntrospeQL and should not be edited directly. */',
44
60
  types: {
45
- 'public.geography': 'Geography'
46
- },
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
+ }
50
71
  ```
51
72
 
52
- ## Example Output
73
+ Create a `.gitignore` file at the root of your project and make sure it includes
74
+ `node_modules` and `.env`:
53
75
 
54
- ```typescript
55
- // src/db/types.ts
76
+ ```
77
+ # .gitignore
78
+ node_modules/
79
+ .env
80
+ ```
56
81
 
57
- /* This file was generated by IntrospeQL. Do not edit manually. */
58
- // Custom header:
59
- import type { Geography } from '../model/geography';
82
+ Create a `.env` file at the root of your project and add your database connection
83
+ information:
60
84
 
61
- export namespace Public {
62
- export const SchemaName = 'public';
85
+ ```
86
+ # .env
87
+ DB_HOST= # Add your host here, often just localhost
88
+ DB_PORT= # Add your port here
89
+ DB_NAME= # Add the name of the database you wish to connect to here
90
+ DB_USER= # Add the PostgreSQL username that should be used to connect to the db
91
+ DB_PASSWORD= # Add the password for this user here
92
+ ```
63
93
 
64
- export namespace Enums {
65
- export enum DistanceUnits {
66
- METERS = 'METERS',
67
- KILOMETERS = 'KILOMETERS',
68
- MILES = 'MILES',
69
- }
70
- }
94
+ Execute your script:
71
95
 
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
- }
96
+ ```
97
+ npm run gen-types
98
+ ```
97
99
 
98
- export namespace Procedures {
99
- export namespace DistanceWithUnits {
100
- export const ProcedureName = 'distance_with_units';
100
+ A type definition file will have been generated at `generated/type-definitions.ts`.
101
101
 
102
- export const ArgNames = ['point_a', 'point_b', 'units'] as const;
102
+ > ⚠️ **Important**
103
+ > Never hard code sensitive information directly into your project. Instead,
104
+ > load such information from environment variables and ensure that .env files
105
+ > are not committed to source control.
103
106
 
104
- export type ArgTypes = [Geography, Geography, Enums.DistanceUnits];
107
+ ## Output
105
108
 
106
- export type ReturnType = number;
107
- }
108
- }
109
- }
109
+ The type definitions file consists of nested
110
+ [namespaces](https://www.typescriptlang.org/docs/handbook/namespaces.html).
111
+ Each top-level namespace represents a schema and contains the name of the schema
112
+ as well as type definitions for its tables, views, materialized views,
113
+ functions, and enums, grouped by category into nested namespaces. Each table,
114
+ view, materialized view and function, in turn, consists of a namespace.
115
+
116
+ Relation (tables, views, and materialized views) namespaces contain the name of
117
+ the relation, a union of string literals representing the names of its columns,
118
+ and an object-type representing the structure of each of its rows.
119
+
120
+ Function namespaces contain the name of the function and an array of overloads.
121
+ Each overload contains an array of parameter types and (separately from the
122
+ array of parameter types) the return type.
123
+
124
+ Enums are represented as unions of string literals, with each string literal
125
+ representing one possible value of the enum.
126
+
127
+ A header can be added to the file. This is useful to import types that will be
128
+ used later in the file. In the example, we simply added a comment to the top of
129
+ the file indicating that the file was autogenerated and should not be changed
130
+ manually.
131
+
132
+ ## Configuration
133
+
134
+ ### Root-Level Configuration
135
+
136
+ The following options exist directly on the top-level configuration object.
137
+
138
+ | Option | Type | Required | Default | Description |
139
+ | -------------------- | ------------------------- | ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
140
+ | `schemas` | `string[]` | No | `['public']` | PostgreSQL schemas to introspect. At least one schema must be specified. |
141
+ | `header` | `string` | No | — | Text inserted at the top of the generated output file (e.g., additional type imports or definitions). |
142
+ | `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'` |
143
+ | `copyComments` | `boolean` | No | `true` | Whether database comments are copied into generated TypeScript documentation comments. |
144
+ | `dbConnectionString` | `string` | Conditional | — | PostgreSQL connection string. Exactly one of this or `dbConnectionParams` must be provided. |
145
+ | `dbConnectionParams` | `object` | Conditional | — | PostgreSQL connection parameters. Exactly one of this or `dbConnectionString` must be provided. |
146
+ | `writeToDisk` | `boolean` | Yes | — | Controls whether generated output is written to disk. |
147
+ | `outFile` | `string` | Conditional | — | Output file path. Required when `writeToDisk` is `true`. |
148
+ | `tables` | `TableOptions` | No | — | Table filtering configuration. |
149
+ | `views` | `ViewOptions` | No | — | View filtering configuration. |
150
+ | `materializedViews` | `MaterializedViewOptions` | No | — | Materialized view filtering configuration. |
151
+ | `functions` | `FunctionOptions` | No | — | Function filtering and typing configuration. |
152
+
153
+ #### `dbConnectionParams`
154
+
155
+ | Option | Type | Required | Description |
156
+ | ---------- | -------- | -------- | ------------------- |
157
+ | `user` | `string` | No | Database user name. |
158
+ | `password` | `string` | No | Database password. |
159
+ | `host` | `string` | No | Database host. |
160
+ | `port` | `number` | No | Database port. |
161
+ | `database` | `string` | No | Database name. |
162
+
163
+ ---
164
+
165
+ ### Table Options
166
+
167
+ Controls which database tables are included in the generated output.
168
+
169
+ | Option | Type | Required | Default | Description |
170
+ | --------------- | ---------------------------- | -------- | ------------- | --------------------------------------------------- |
171
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how table filtering is applied. |
172
+ | `excludeTables` | `string[]` | No | `[]` | Tables to exclude when operating in inclusive mode. |
173
+ | `includeTables` | `string[]` | No | `[]` | Tables to include when operating in exclusive mode. |
174
+
175
+ ---
176
+
177
+ ### View Options
178
+
179
+ Controls which views are included in the generated output.
180
+
181
+ | Option | Type | Required | Default | Description |
182
+ | -------------- | ---------------------------- | -------- | ------------- | -------------------------------------------------- |
183
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how view filtering is applied. |
184
+ | `excludeViews` | `string[]` | No | `[]` | Views to exclude when operating in inclusive mode. |
185
+ | `includeViews` | `string[]` | No | `[]` | Views to include when operating in exclusive mode. |
186
+
187
+ ---
188
+
189
+ ### Materialized View Options
190
+
191
+ Controls which materialized views are included in the generated output.
192
+
193
+ | Option | Type | Required | Default | Description |
194
+ | -------------------------- | ---------------------------- | -------- | ------------- | --------------------------------------------------------------- |
195
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how materialized view filtering is applied. |
196
+ | `excludeMaterializedViews` | `string[]` | No | `[]` | Materialized views to exclude when operating in inclusive mode. |
197
+ | `includeMaterializedViews` | `string[]` | No | `[]` | Materialized views to include when operating in exclusive mode. |
198
+
199
+ ---
200
+
201
+ ### Function Options
202
+
203
+ Controls which PostgreSQL functions are included and how their types are generated.
204
+
205
+ | Option | Type | Required | Default | Description |
206
+ | --------------------- | ---------------------------- | -------- | ------------- | ----------------------------------------------------------- |
207
+ | `mode` | `'inclusive' \| 'exclusive'` | No | `'inclusive'` | Determines how function filtering is applied. |
208
+ | `excludeFunctions` | `string[]` | No | `[]` | Functions to exclude when operating in inclusive mode. |
209
+ | `includeFunctions` | `string[]` | No | `[]` | Functions to include when operating in exclusive mode. |
210
+ | `nullableArgs` | `boolean` | No | `false` | Treat function arguments as nullable in generated types. |
211
+ | `nullableReturnTypes` | `boolean` | No | `true` | Treat function return types as nullable in generated types. |
212
+
213
+ ## Types
214
+
215
+ Default type mappings are based on the types produced by
216
+ [node-postgres](https://node-postgres.com/). These defaults can be added to or
217
+ overridden using configuration options described above. Type definitions for
218
+ enums will be generated automatically, provided that the enum is used somewhere
219
+ in a table, view, materialized view, or function definition.
220
+
221
+ To define custom types, use the header to import or define them and use the
222
+ `types` configuration option to map PostgreSQL types to your custom types.
223
+
224
+ Multi-dimensional-array-type columns are recognized. Columns to which a `NOT NULL`
225
+ constraint has been applied will be non-nullable. All columns of a view or
226
+ materialized view will typically be nullable. Optional and variadic function
227
+ parameters are recognized.
228
+
229
+ By default, node-postgres will transform the return value of a function that
230
+ returns void into an empty string. Therefore, by default, the generated
231
+ TypeScript return type of PostgreSQL functions that return void will be
232
+ `string`. If desired, this can be overridden using the `types` configuration
233
+ option.
234
+
235
+ The default value for unrecognized types is `string`.
236
+
237
+ ## Functions
238
+
239
+ Only functions whose `prokind` is `'f'` in `pg_catalog.pg_proc` are recognized
240
+ (i.e. normal functions, not procedures or aggregate functions).
241
+
242
+ Function overloads are recognized.
243
+
244
+ ## Directives
245
+
246
+ IntrospeQL supports the inclusion of several directives which, when included in
247
+ a PostgreSQL comment, can modify how IntrospeQL interacts with the database
248
+ object to which that comment is applied.
249
+
250
+ Directives are case-insensitive but must be separated from each other and from
251
+ other text by a whitespace character (a space, newline, etc).
252
+
253
+ Here is an example of adding a directive to the `config_key` column of a table
254
+ called `config` in the `reporting` schema:
255
+
256
+ ```
257
+ COMMENT ON COLUMN reporting.config.config_key IS
258
+ '@introspeql-enable-tsdoc-comments
259
+ Unique configuration key.';
260
+ ```
261
+
262
+ Below, please find a table of all valid directives:
263
+
264
+ | Category | Directive | Effect |
265
+ | -------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
266
+ | Objects | @introspeql-exclude | Excludes a table, view, materialized view, or function overload when mode is `'inclusive'` |
267
+ | Objects | @introspeql-include | Includes a table, view, materialized view, or function overload when mode is `'exclusive'` |
268
+ | 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` |
269
+ | 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` |
270
+ | 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` |
271
+ | 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` |
272
+ | Comments | @introspeql-enable-tsdoc-comments | Copies the comments of the table, view, materialized view, 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. |
273
+ | Comments | @introspeql-disable-tsdoc-comments | Ignores the comments of the table, view, materialized view, 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. |
274
+ | 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. |
275
+ | 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. |
276
+
277
+ The package also exports a TypeScript enum of all valid directives:
278
+
279
+ ```
280
+ import { Directives } from 'introspeql';
110
281
  ```
111
282
 
283
+ ## Comments
284
+
285
+ [Comments](https://www.postgresql.org/docs/current/sql-comment.html) can be
286
+ applied to PostgreSQL database objects with the following syntax:
287
+
288
+ ```
289
+ COMMENT ON COLUMN auth.users.user_id IS
290
+ 'Unique identifier for the user.';
291
+ ```
292
+
293
+ If IntrospeQL is configured to copy the comments for a given table, view,
294
+ materialized view, enum, or column, PostgreSQL comments applied to that database
295
+ object will be copied into the type definition file as [TSDoc](https://tsdoc.org/)
296
+ comments.
297
+
298
+ Note that all `@introspeql-` directives will always be removed when the copied
299
+ comment is formatted.
300
+
301
+ Comments cannot contain `*/` or a combination of
302
+ characters that results in `*/` after directives are removed. Such comments
303
+ will trigger a warning and will not be included in the output.
304
+
305
+ Assuming comment copying is enabled for a particular database object, you can
306
+ use the `@introspeql-begin-tsdoc-comment` and `@introspeql-end-tsdoc-comment`
307
+ directives to copy only certain excerpts of the comment:
308
+
309
+ ### No Directive
310
+
311
+ If neither `@introspeql-begin-tsdoc-comment` nor `@introspeql-end-tsdoc-comment`
312
+ is included, the entire comment will be copied.
313
+
314
+ ### Using Only `@introspeql-begin-tsdoc-comment`
315
+
316
+ If only `@introspeql-begin-tsdoc-comment` is included, everything before the
317
+ directive will be excluded, and everything after it will be included.
318
+
319
+ ### Using Only `@introspeql-end-tsdoc-comment`
320
+
321
+ If only `@introspeql-end-tsdoc-comment` is included, everything before the
322
+ directive will be included, and everything after it will be excluded.
323
+
324
+ ### Using Both Directives
325
+
326
+ You can alternate `@introspeql-begin-tsdoc-comment` and
327
+ `@introspeql-end-tsdoc-comment`.
328
+
329
+ You can begin or end with either directive, but you cannot include multiple
330
+ instances of the same directive in a row within the same comment (in this case,
331
+ a warning will be printed to the console and the comment will
332
+ <strong>not</strong> be copied).
333
+
334
+ When alternating between directives, content before
335
+ `@introspeql-end-tsdoc-comment` or after `@introspeql-begin-tsdoc-comment` will
336
+ be included and all other content will be excluded.
337
+
338
+ ## Contributing
339
+
340
+ If you notice a bug or would like to request a feature, please submit an issue.
341
+
112
342
  ## License
113
343
 
114
344
  MIT License
@@ -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';