@rvoh/dream 2.0.4 → 2.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/dist/cjs/src/bin/index.js +2 -0
- package/dist/cjs/src/db/helpers/dreamSchemaTypesFilenameForConnection.js +3 -0
- package/dist/cjs/src/db/helpers/syncDbTypesFiles.js +2 -89
- package/dist/cjs/src/dream/QueryDriver/Base.js +2 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +9 -19
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/cjs/src/dream-app/index.js +15 -0
- package/dist/cjs/src/helpers/cli/ASTBuilder.js +277 -0
- package/dist/cjs/src/helpers/cli/ASTConnectionBuilder.js +284 -0
- package/dist/cjs/src/helpers/cli/ASTGlobalSchemaBuilder.js +57 -0
- package/dist/cjs/src/helpers/cli/ASTKyselyCodegenEnhancer.js +236 -0
- package/dist/cjs/src/helpers/cli/ASTSchemaBuilder.js +304 -0
- package/dist/cjs/src/helpers/cli/DBClassDeprecation.js +60 -0
- package/dist/esm/src/bin/index.js +2 -0
- package/dist/esm/src/db/helpers/dreamSchemaTypesFilenameForConnection.js +3 -0
- package/dist/esm/src/db/helpers/syncDbTypesFiles.js +2 -89
- package/dist/esm/src/dream/QueryDriver/Base.js +2 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +9 -19
- package/dist/esm/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/esm/src/dream-app/index.js +15 -0
- package/dist/esm/src/helpers/cli/ASTBuilder.js +277 -0
- package/dist/esm/src/helpers/cli/ASTConnectionBuilder.js +284 -0
- package/dist/esm/src/helpers/cli/ASTGlobalSchemaBuilder.js +57 -0
- package/dist/esm/src/helpers/cli/ASTKyselyCodegenEnhancer.js +236 -0
- package/dist/esm/src/helpers/cli/ASTSchemaBuilder.js +304 -0
- package/dist/esm/src/helpers/cli/DBClassDeprecation.js +60 -0
- package/dist/types/src/db/helpers/dreamSchemaTypesFilenameForConnection.d.ts +1 -0
- package/dist/types/src/dream/QueryDriver/Base.d.ts +3 -3
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +2 -14
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +2 -2
- package/dist/types/src/dream-app/index.d.ts +12 -2
- package/dist/types/src/helpers/cli/ASTBuilder.d.ts +159 -0
- package/dist/types/src/helpers/cli/ASTConnectionBuilder.d.ts +104 -0
- package/dist/types/src/helpers/cli/ASTGlobalSchemaBuilder.d.ts +28 -0
- package/dist/types/src/helpers/cli/ASTKyselyCodegenEnhancer.d.ts +68 -0
- package/dist/types/src/helpers/cli/ASTSchemaBuilder.d.ts +80 -0
- package/dist/types/src/helpers/cli/DBClassDeprecation.d.ts +14 -0
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +9 -9
- package/docs/classes/db.KyselyQueryDriver.html +31 -44
- package/docs/classes/db.PostgresQueryDriver.html +32 -45
- package/docs/classes/db.QueryDriverBase.html +30 -30
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +2 -2
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +113 -113
- package/docs/classes/index.DreamApp.html +7 -6
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +53 -53
- package/docs/classes/system.CliFileWriter.html +2 -2
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +5 -5
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +8 -8
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/utils.Encrypt.html +2 -2
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range-1.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/modules/db.html +1 -1
- package/docs/modules/errors.html +1 -1
- package/docs/modules/index.html +1 -1
- package/docs/modules/openapi.html +1 -1
- package/docs/modules/system.html +1 -1
- package/docs/modules/types.html +1 -1
- package/docs/modules/utils.html +1 -1
- package/docs/types/index.DateTime.html +1 -1
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +1 -1
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +1 -1
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DateTime-1.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes-1.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes-1.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/types.TRIGRAM_OPERATORS.html +1 -1
- package/docs/variables/types.primaryKeyTypes.html +1 -1
- package/package.json +1 -1
- package/dist/cjs/src/helpers/cli/SchemaBuilder.js +0 -408
- package/dist/esm/src/helpers/cli/SchemaBuilder.js +0 -408
- package/dist/types/src/helpers/cli/SchemaBuilder.d.ts +0 -44
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
import dbTypesFilenameForConnection from '../../db/helpers/dbTypesFilenameForConnection.js';
|
|
5
|
+
import dreamSchemaTypesFilenameForConnection from '../../db/helpers/dreamSchemaTypesFilenameForConnection.js';
|
|
6
|
+
import DreamApp from '../../dream-app/index.js';
|
|
7
|
+
import Query from '../../dream/Query.js';
|
|
8
|
+
import { ExplicitForeignKeyRequired, InvalidComputedForeignKey, } from '../../errors/associations/InvalidComputedForeignKey.js';
|
|
9
|
+
import FailedToIdentifyAssociation from '../../errors/schema-builder/FailedToIdentifyAssociation.js';
|
|
10
|
+
import intersection from '../intersection.js';
|
|
11
|
+
import sortBy from '../sortBy.js';
|
|
12
|
+
import uniq from '../uniq.js';
|
|
13
|
+
import ASTBuilder from './ASTBuilder.js';
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* This is a base class, which is inherited by the ASTSchemaBuilder and
|
|
18
|
+
* the ASTKyselyCodegenEnhancer, both of which is responsible for building
|
|
19
|
+
* up the output of the various type files consumed by dream internally.
|
|
20
|
+
*
|
|
21
|
+
* This base class is just a container for common methods used by both
|
|
22
|
+
* classes. It requires a connectionName to be provided, unlike the underlying
|
|
23
|
+
* ASTBuilder class, and provides methods which leverage the connectionName
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
export default class ASTConnectionBuilder extends ASTBuilder {
|
|
27
|
+
connectionName;
|
|
28
|
+
hasForeignKeyError = false;
|
|
29
|
+
constructor(connectionName) {
|
|
30
|
+
super();
|
|
31
|
+
this.connectionName = connectionName;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @internal
|
|
35
|
+
*
|
|
36
|
+
* returns the path from project root to the dream.ts file
|
|
37
|
+
* for the particular connection. If the connectionName is anything
|
|
38
|
+
* other than default, the path will represent that by injecting
|
|
39
|
+
* the connectionName into the file name, i.e. dream.alternate.ts
|
|
40
|
+
*/
|
|
41
|
+
schemaPath() {
|
|
42
|
+
const dreamApp = DreamApp.getOrFail();
|
|
43
|
+
return path.join(dreamApp.projectRoot, dreamApp.paths.types, dreamSchemaTypesFilenameForConnection(this.connectionName));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @internal
|
|
47
|
+
*
|
|
48
|
+
* returns the path from project root to the db.ts file
|
|
49
|
+
* for the particular connection. If the connectionName is anything
|
|
50
|
+
* other than default, the path will represent that by injecting
|
|
51
|
+
* the connectionName into the file name, i.e. db.alternate.ts
|
|
52
|
+
*/
|
|
53
|
+
dbPath() {
|
|
54
|
+
const dreamApp = DreamApp.getOrFail();
|
|
55
|
+
return path.join(dreamApp.projectRoot, dreamApp.paths.types, dbTypesFilenameForConnection(this.connectionName));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @internal
|
|
59
|
+
*
|
|
60
|
+
* returns the db source file for the given connectionName, injecting
|
|
61
|
+
* the source file with the actual file contents, so that AST nodes
|
|
62
|
+
* can be built through ingesting.
|
|
63
|
+
*/
|
|
64
|
+
async getDbSourceFile() {
|
|
65
|
+
const fileContent = await this.loadDbSyncFile();
|
|
66
|
+
return ts.createSourceFile('./db.js', fileContent, ts.ScriptTarget.Latest, true);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @internal
|
|
70
|
+
*
|
|
71
|
+
* reads the db source file for the given connection, returning the contents
|
|
72
|
+
* as a raw string
|
|
73
|
+
*/
|
|
74
|
+
async loadDbSyncFile() {
|
|
75
|
+
return (await fs.readFile(this.dbPath())).toString();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @internal
|
|
79
|
+
*
|
|
80
|
+
* builds up the schema data for every table into an object, which
|
|
81
|
+
* can be read and injected into AST nodes.
|
|
82
|
+
*/
|
|
83
|
+
async getSchemaData() {
|
|
84
|
+
const tables = await this.getTables();
|
|
85
|
+
const schemaData = {};
|
|
86
|
+
for (const table of tables) {
|
|
87
|
+
schemaData[table] = await this.tableData(table);
|
|
88
|
+
}
|
|
89
|
+
return schemaData;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* @internal
|
|
93
|
+
*
|
|
94
|
+
* used by getSchemaData to build up all table data
|
|
95
|
+
*/
|
|
96
|
+
async getTables() {
|
|
97
|
+
const fileContents = await this.loadDbSyncFile();
|
|
98
|
+
const tableLines = /export interface DB {([^}]*)}/.exec(fileContents)[1];
|
|
99
|
+
if (tableLines === undefined)
|
|
100
|
+
return [];
|
|
101
|
+
const tables = tableLines
|
|
102
|
+
.split('\n')
|
|
103
|
+
.map(line => {
|
|
104
|
+
const stingArray = line.split(':');
|
|
105
|
+
const substring = stingArray[0];
|
|
106
|
+
if (substring === undefined)
|
|
107
|
+
return '';
|
|
108
|
+
return substring.replace(/\s*/, '');
|
|
109
|
+
})
|
|
110
|
+
.filter(line => !!line);
|
|
111
|
+
return tables;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @internal
|
|
115
|
+
*
|
|
116
|
+
* finds all enums used by the app, and returns information
|
|
117
|
+
* about those enums that can be used for type generating purpposes
|
|
118
|
+
*/
|
|
119
|
+
async getAllEnumValueNames() {
|
|
120
|
+
const schemaData = await this.getSchemaData();
|
|
121
|
+
const enumValueNames = Object.values(schemaData)
|
|
122
|
+
.map(tableData => Object.keys(tableData.columns)
|
|
123
|
+
.filter(columnName => !!tableData.columns[columnName]?.enumValues)
|
|
124
|
+
.map(columnName => ({
|
|
125
|
+
enumValues: tableData.columns[columnName].enumValues,
|
|
126
|
+
enumType: tableData.columns[columnName].enumType,
|
|
127
|
+
})))
|
|
128
|
+
.flat();
|
|
129
|
+
return enumValueNames;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* @internal
|
|
133
|
+
*
|
|
134
|
+
* returns a tuple, where the first value is the global name, and the second value
|
|
135
|
+
* is the table that that global name points to. Used to build up our global
|
|
136
|
+
* model name exports within type files.
|
|
137
|
+
*/
|
|
138
|
+
globalModelNames() {
|
|
139
|
+
const dreamApp = DreamApp.getOrFail();
|
|
140
|
+
const models = dreamApp.models;
|
|
141
|
+
return Object.keys(models)
|
|
142
|
+
.filter(key => models[key]?.prototype?.connectionName === this.connectionName)
|
|
143
|
+
.map(key => [key, models[key].prototype.table]);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* @internal
|
|
147
|
+
*
|
|
148
|
+
* retrieves useful association data for a given association and table, which
|
|
149
|
+
* can be used to build up types
|
|
150
|
+
*/
|
|
151
|
+
getAssociationData(tableName, targetAssociationType) {
|
|
152
|
+
const dreamApp = DreamApp.getOrFail();
|
|
153
|
+
const models = sortBy(Object.values(dreamApp.models), m => m.table);
|
|
154
|
+
const tableAssociationData = {};
|
|
155
|
+
for (const model of models.filter(model => model.table === tableName)) {
|
|
156
|
+
for (const associationName of model.associationNames) {
|
|
157
|
+
const associationMetaData = model['associationMetadataMap']()[associationName];
|
|
158
|
+
if (associationMetaData === undefined)
|
|
159
|
+
continue;
|
|
160
|
+
if (targetAssociationType && associationMetaData.type !== targetAssociationType)
|
|
161
|
+
continue;
|
|
162
|
+
const dreamClassOrClasses = associationMetaData.modelCB();
|
|
163
|
+
if (!dreamClassOrClasses)
|
|
164
|
+
throw new FailedToIdentifyAssociation(model, associationMetaData.type, associationName, associationMetaData.globalAssociationNameOrNames);
|
|
165
|
+
const optional = associationMetaData.type === 'BelongsTo' ? associationMetaData.optional === true : null;
|
|
166
|
+
const where = associationMetaData.type === 'HasMany' || associationMetaData.type === 'HasOne'
|
|
167
|
+
? associationMetaData.and || null
|
|
168
|
+
: null;
|
|
169
|
+
// NOTE
|
|
170
|
+
// this try-catch is here because the ASTSchemaBuilder currently needs to be run twice to generate foreignKey
|
|
171
|
+
// correctly. The first time will raise, since calling Dream.columns is dependant on the schema const to
|
|
172
|
+
// introspect columns during a foreign key check. This will be repaired once kysely types have been successfully
|
|
173
|
+
// split off into a separate file from the types we diliver in types/dream.ts
|
|
174
|
+
let foreignKey = null;
|
|
175
|
+
try {
|
|
176
|
+
const isThroughAssociation = associationMetaData.through;
|
|
177
|
+
if (!isThroughAssociation) {
|
|
178
|
+
const _foreignKey = associationMetaData.foreignKey();
|
|
179
|
+
foreignKey = _foreignKey;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
this.hasForeignKeyError = true;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
tableAssociationData[associationName] ||= {
|
|
187
|
+
tables: [],
|
|
188
|
+
type: associationMetaData.type,
|
|
189
|
+
polymorphic: associationMetaData.polymorphic,
|
|
190
|
+
foreignKey,
|
|
191
|
+
foreignKeyTypeColumn: associationMetaData.polymorphic
|
|
192
|
+
? associationMetaData?.foreignKeyTypeField?.() || null
|
|
193
|
+
: null,
|
|
194
|
+
optional,
|
|
195
|
+
and: where,
|
|
196
|
+
};
|
|
197
|
+
if (foreignKey)
|
|
198
|
+
tableAssociationData[associationName]['foreignKey'] = foreignKey;
|
|
199
|
+
if (Array.isArray(dreamClassOrClasses)) {
|
|
200
|
+
const tables = dreamClassOrClasses.map(dreamClass => dreamClass.table);
|
|
201
|
+
tableAssociationData[associationName].tables = [
|
|
202
|
+
...tableAssociationData[associationName].tables,
|
|
203
|
+
...tables,
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
tableAssociationData[associationName].tables.push(dreamClassOrClasses.table);
|
|
208
|
+
}
|
|
209
|
+
// guarantee unique
|
|
210
|
+
tableAssociationData[associationName].tables = [
|
|
211
|
+
...new Set(tableAssociationData[associationName].tables),
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (!(error instanceof ExplicitForeignKeyRequired || error instanceof InvalidComputedForeignKey))
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return Object.keys(tableAssociationData)
|
|
221
|
+
.sort()
|
|
222
|
+
.reduce((acc, key) => {
|
|
223
|
+
if (tableAssociationData[key] === undefined)
|
|
224
|
+
return acc;
|
|
225
|
+
acc[key] = tableAssociationData[key];
|
|
226
|
+
return acc;
|
|
227
|
+
}, {});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* @internal
|
|
231
|
+
*
|
|
232
|
+
* retrieves the table data for an individual table.
|
|
233
|
+
* Can be used to build up types
|
|
234
|
+
*/
|
|
235
|
+
async tableData(tableName) {
|
|
236
|
+
const dreamApp = DreamApp.getOrFail();
|
|
237
|
+
const models = Object.values(dreamApp.models).filter(model => model.table === tableName);
|
|
238
|
+
const maybeModel = models[0];
|
|
239
|
+
if (!maybeModel)
|
|
240
|
+
throw new Error(`
|
|
241
|
+
Could not find a Dream model with table "${tableName}".
|
|
242
|
+
|
|
243
|
+
If you recently changed the name of a table in a migration, you
|
|
244
|
+
may need to update the table getter in the corresponding Dream.
|
|
245
|
+
`);
|
|
246
|
+
const baseModel = maybeModel['stiBaseClassOrOwnClass'];
|
|
247
|
+
const associationData = this.getAssociationData(tableName);
|
|
248
|
+
const allStiChildren = models.filter(model => model['isSTIChild']);
|
|
249
|
+
const modelsToCheck = allStiChildren.length ? allStiChildren : [baseModel];
|
|
250
|
+
// If a table is STI, then we look only at the serializers attached to
|
|
251
|
+
// all STI children (not the STI base model because the base model may not have any serializers)
|
|
252
|
+
const eachModelSerializerKeys = modelsToCheck.map(model => {
|
|
253
|
+
let serializers = {};
|
|
254
|
+
try {
|
|
255
|
+
serializers = model?.prototype?.['serializers'] || {};
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// no-op
|
|
259
|
+
}
|
|
260
|
+
return Object.keys(serializers);
|
|
261
|
+
});
|
|
262
|
+
const serializerKeys = intersection(...eachModelSerializerKeys).sort();
|
|
263
|
+
return {
|
|
264
|
+
scopes: {
|
|
265
|
+
default: uniq(models.flatMap(model => model['scopes'].default.map(scopeStatement => scopeStatement.method))),
|
|
266
|
+
named: uniq(models.flatMap(model => model['scopes'].named.map(scopeStatement => scopeStatement.method))),
|
|
267
|
+
},
|
|
268
|
+
columns: await this.getColumnData(tableName, associationData),
|
|
269
|
+
virtualColumns: uniq(models.flatMap(model => model['virtualAttributes'].map(prop => prop.property) || [])),
|
|
270
|
+
associations: associationData,
|
|
271
|
+
serializerKeys,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* @internal
|
|
276
|
+
*
|
|
277
|
+
* retrieves the column data for an individual table and association.
|
|
278
|
+
* Can be used to build up types
|
|
279
|
+
*/
|
|
280
|
+
async getColumnData(tableName, allTableAssociationData) {
|
|
281
|
+
const dbDriverClass = Query.dbDriverClass(this.connectionName);
|
|
282
|
+
return await dbDriverClass.getColumnData(this.connectionName, tableName, allTableAssociationData);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { CliFileWriter } from '../../cli/CliFileWriter.js';
|
|
3
|
+
import ASTBuilder from './ASTBuilder.js';
|
|
4
|
+
import autogeneratedFileDisclaimer from './autoGeneratedFileDisclaimer.js';
|
|
5
|
+
const f = ts.factory;
|
|
6
|
+
/**
|
|
7
|
+
* Responsible for building dream globals, which can be found at
|
|
8
|
+
* types/dream.globals.ts.
|
|
9
|
+
*
|
|
10
|
+
* This class leverages internal AST building mechanisms built into
|
|
11
|
+
* typescript to manually build up object literals and interfaces
|
|
12
|
+
* for our app to consume.
|
|
13
|
+
*/
|
|
14
|
+
export default class ASTGlobalSchemaBuilder extends ASTBuilder {
|
|
15
|
+
async build() {
|
|
16
|
+
// build a new, blank source file to populate with our output
|
|
17
|
+
const sourceFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
18
|
+
const output = await this.prettier(this.printStatements([this.buildGlobalTypeConfigConst()], sourceFile));
|
|
19
|
+
await CliFileWriter.write(this.globalSchemaPath(), output);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*
|
|
24
|
+
* builds up the `export const globalTypeConfig = ...` statement within the dream.globals.ts
|
|
25
|
+
* file. It does this by leveraging low-level AST utils built into typescript
|
|
26
|
+
* to manually build up an object literal, cast it as a const, and write it to
|
|
27
|
+
* an exported variable.
|
|
28
|
+
*/
|
|
29
|
+
buildGlobalTypeConfigConst() {
|
|
30
|
+
const globalTypeConfigObjectLiteral = f.createObjectLiteralExpression([
|
|
31
|
+
f.createPropertyAssignment(f.createIdentifier('serializers'), f.createArrayLiteralExpression(this.globalSerializerNames()
|
|
32
|
+
.sort()
|
|
33
|
+
.map(key => f.createStringLiteral(key)))),
|
|
34
|
+
], true // multiline
|
|
35
|
+
);
|
|
36
|
+
// add "as const" to the end of the schema object we
|
|
37
|
+
// have built before returning it
|
|
38
|
+
const constAssertion = f.createAsExpression(globalTypeConfigObjectLiteral, f.createKeywordTypeNode(ts.SyntaxKind.ConstKeyword));
|
|
39
|
+
const globalTypeConfigObjectLiteralConst = f.createVariableStatement([f.createModifier(ts.SyntaxKind.ExportKeyword)], f.createVariableDeclarationList([
|
|
40
|
+
f.createVariableDeclaration(f.createIdentifier('globalTypeConfig'), undefined, undefined, constAssertion),
|
|
41
|
+
], ts.NodeFlags.Const));
|
|
42
|
+
return globalTypeConfigObjectLiteralConst;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @internal
|
|
46
|
+
*
|
|
47
|
+
* writes the compiled statements to string.
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
printStatements(statements, sourceFile) {
|
|
51
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, omitTrailingSemicolon: true });
|
|
52
|
+
const result = printer.printList(ts.ListFormat.SourceFileStatements, f.createNodeArray(statements), sourceFile);
|
|
53
|
+
return `\
|
|
54
|
+
${autogeneratedFileDisclaimer()}
|
|
55
|
+
${result}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { CliFileWriter } from '../../cli/CliFileWriter.js';
|
|
3
|
+
import camelize from '../camelize.js';
|
|
4
|
+
import ASTConnectionBuilder from './ASTConnectionBuilder.js';
|
|
5
|
+
import autogeneratedFileDisclaimer from './autoGeneratedFileDisclaimer.js';
|
|
6
|
+
const f = ts.factory;
|
|
7
|
+
/**
|
|
8
|
+
* Responsible for enhancing the kysely-codegen output, which can be found at
|
|
9
|
+
* types/db.ts. If you are leveraging multiple db connections,
|
|
10
|
+
* then this file will also be responsible for building all variants,
|
|
11
|
+
* i.e. types/db.alternateConnection.ts, etc...
|
|
12
|
+
*
|
|
13
|
+
* This class leverages internal AST building mechanisms built into
|
|
14
|
+
* typescript to manually build up object literals and interfaces
|
|
15
|
+
* for our app to consume.
|
|
16
|
+
*/
|
|
17
|
+
export default class ASTKyselyCodegenEnhancer extends ASTConnectionBuilder {
|
|
18
|
+
/**
|
|
19
|
+
* enhances the kysely codegen output for the given connection name
|
|
20
|
+
* by reading the relevant db.ts file into AST nodes, then enhancing them
|
|
21
|
+
* with a variety of transformations necessary to lock this file in with
|
|
22
|
+
* our other schema files, allowing types to flow correctly throughout the app.
|
|
23
|
+
*/
|
|
24
|
+
async enhance() {
|
|
25
|
+
let dbSourceFile = await this.getDbSourceFile();
|
|
26
|
+
dbSourceFile = this.camelizeKeys(dbSourceFile);
|
|
27
|
+
dbSourceFile = this.replaceTimestampExport(dbSourceFile);
|
|
28
|
+
dbSourceFile = this.addMissingImports(dbSourceFile);
|
|
29
|
+
dbSourceFile = this.replaceInt8Export(dbSourceFile);
|
|
30
|
+
dbSourceFile = this.sortExportedInterfacesTransformer(dbSourceFile);
|
|
31
|
+
dbSourceFile = await this.addEnumValueExports(dbSourceFile);
|
|
32
|
+
dbSourceFile = this.addDeprecatedDbClassExportForBackwardsCompatibility(dbSourceFile);
|
|
33
|
+
const output = this.printOutput(dbSourceFile);
|
|
34
|
+
const finalOutput = await this.prettier(`\
|
|
35
|
+
${autogeneratedFileDisclaimer()}
|
|
36
|
+
${output}`);
|
|
37
|
+
await CliFileWriter.write(this.dbPath(), finalOutput);
|
|
38
|
+
}
|
|
39
|
+
addDeprecatedDbClassExportForBackwardsCompatibility(dbSourceFile) {
|
|
40
|
+
const transformer = () => {
|
|
41
|
+
return sourceFile => {
|
|
42
|
+
const dbInterface = this.findDbExport(sourceFile, 'DB');
|
|
43
|
+
if (!dbInterface)
|
|
44
|
+
return sourceFile;
|
|
45
|
+
const classDec = ts.factory.createClassDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier('DBClass'), undefined, [], dbInterface.members.filter(ts.isPropertySignature).map(propertySignature => {
|
|
46
|
+
return f.createPropertyDeclaration([], propertySignature.name, undefined, propertySignature.type, undefined);
|
|
47
|
+
}));
|
|
48
|
+
return ts.factory.updateSourceFile(sourceFile, [...sourceFile.statements, classDec], sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, undefined);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
52
|
+
return result.transformed[0];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* @internal
|
|
56
|
+
*
|
|
57
|
+
* returns the output of the provided dbSourceFile, ensuring that all exports
|
|
58
|
+
* have a new line above them.
|
|
59
|
+
*/
|
|
60
|
+
printOutput(dbSourceFile) {
|
|
61
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
62
|
+
const output = printer.printNode(ts.EmitHint.SourceFile, dbSourceFile, dbSourceFile);
|
|
63
|
+
return output.replace(/export /g, '\nexport ');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @internal
|
|
67
|
+
*
|
|
68
|
+
* ensures that the `Timestamp` exported from db.ts points correctly to either
|
|
69
|
+
* the DateTime or CalendarDate class, depending on the db type.
|
|
70
|
+
*/
|
|
71
|
+
replaceTimestampExport(dbSourceFile) {
|
|
72
|
+
const transformer = context => {
|
|
73
|
+
return rootNode => {
|
|
74
|
+
const visit = (node) => {
|
|
75
|
+
const typeAlias = this.exportedTypeAliasOrNull(node, 'Timestamp');
|
|
76
|
+
if (typeAlias) {
|
|
77
|
+
const updatedNode = f.updateTypeAliasDeclaration(typeAlias, typeAlias.modifiers, typeAlias.name, typeAlias.typeParameters, f.createTypeReferenceNode('ColumnType', [
|
|
78
|
+
f.createUnionTypeNode([
|
|
79
|
+
f.createTypeReferenceNode(f.createIdentifier('DateTime')),
|
|
80
|
+
f.createTypeReferenceNode(f.createIdentifier('CalendarDate')),
|
|
81
|
+
]),
|
|
82
|
+
]));
|
|
83
|
+
return updatedNode;
|
|
84
|
+
}
|
|
85
|
+
return ts.visitEachChild(node, visit, context);
|
|
86
|
+
};
|
|
87
|
+
return ts.visitNode(rootNode, visit);
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
91
|
+
return result.transformed[0];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* @internal
|
|
95
|
+
*
|
|
96
|
+
* massages the `Int8` exported from db.ts to make it more flexible
|
|
97
|
+
*/
|
|
98
|
+
replaceInt8Export(dbSourceFile) {
|
|
99
|
+
const transformer = context => {
|
|
100
|
+
return rootNode => {
|
|
101
|
+
const visit = (node) => {
|
|
102
|
+
const typeAlias = this.exportedTypeAliasOrNull(node, 'Int8');
|
|
103
|
+
if (typeAlias) {
|
|
104
|
+
return f.updateTypeAliasDeclaration(typeAlias, typeAlias.modifiers, typeAlias.name, typeAlias.typeParameters, f.createTypeReferenceNode('ColumnType', [
|
|
105
|
+
f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
106
|
+
f.createUnionTypeNode([
|
|
107
|
+
f.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword),
|
|
108
|
+
f.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
|
|
109
|
+
f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
110
|
+
]),
|
|
111
|
+
f.createUnionTypeNode([
|
|
112
|
+
f.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword),
|
|
113
|
+
f.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
|
|
114
|
+
f.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
115
|
+
]),
|
|
116
|
+
]));
|
|
117
|
+
}
|
|
118
|
+
return ts.visitEachChild(node, visit, context);
|
|
119
|
+
};
|
|
120
|
+
return ts.visitNode(rootNode, visit);
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
124
|
+
return result.transformed[0];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* @internal
|
|
128
|
+
*
|
|
129
|
+
* injects missing CalendarDate and DateTime imports from dream
|
|
130
|
+
*/
|
|
131
|
+
addMissingImports(dbSourceFile) {
|
|
132
|
+
const transformer = () => {
|
|
133
|
+
return (sourceFile) => f.updateSourceFile(sourceFile, [...this.dateAndDateTimeImports(), ...sourceFile.statements]);
|
|
134
|
+
};
|
|
135
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
136
|
+
return result.transformed[0];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @internal
|
|
140
|
+
*
|
|
141
|
+
* sorts all exported interfaces and puts them above the DB interface
|
|
142
|
+
*/
|
|
143
|
+
sortExportedInterfacesTransformer(dbSourceFile) {
|
|
144
|
+
const transformer = () => {
|
|
145
|
+
return (sourceFile) => {
|
|
146
|
+
const sortedInterfaceStatements = [];
|
|
147
|
+
const finalInterfaceStatements = [];
|
|
148
|
+
const otherStatements = [];
|
|
149
|
+
sourceFile.statements.forEach(statement => {
|
|
150
|
+
if (ts.isInterfaceDeclaration(statement) &&
|
|
151
|
+
statement.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
152
|
+
if (statement.name.text !== 'DB') {
|
|
153
|
+
sortedInterfaceStatements.push(statement);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
finalInterfaceStatements.push(statement);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
otherStatements.push(statement);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
sortedInterfaceStatements.sort((a, b) => a.name.text.localeCompare(b.name.text));
|
|
164
|
+
return f.updateSourceFile(sourceFile, [...otherStatements, ...sortedInterfaceStatements, ...finalInterfaceStatements], sourceFile.isDeclarationFile, sourceFile.referencedFiles);
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
168
|
+
return result.transformed[0];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* @internal
|
|
172
|
+
*
|
|
173
|
+
* camelizes the keys on all interfaces except the DB interface
|
|
174
|
+
* (since the DB interface is indexed by table name, which must not be camelized)
|
|
175
|
+
*/
|
|
176
|
+
camelizeKeys(dbSourceFile) {
|
|
177
|
+
// @ts-expect-error cannot lock this type down, though implementation is correct
|
|
178
|
+
const transformer = context => {
|
|
179
|
+
const visit = node => {
|
|
180
|
+
const interfaceNode = this.exportedInterfaceOrNull(node);
|
|
181
|
+
if (interfaceNode &&
|
|
182
|
+
!['DB'].includes(interfaceNode.name.text) // Check exclusion list
|
|
183
|
+
) {
|
|
184
|
+
const updatedMembers = interfaceNode.members.map(member => {
|
|
185
|
+
if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
|
|
186
|
+
const camelizedKey = camelize(member.name.text);
|
|
187
|
+
if (member.name.text !== camelizedKey) {
|
|
188
|
+
return f.updatePropertySignature(member, member.modifiers, f.createIdentifier(camelizedKey), member.questionToken, member.type);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return member;
|
|
192
|
+
});
|
|
193
|
+
return f.updateInterfaceDeclaration(interfaceNode, interfaceNode.modifiers, interfaceNode.name, interfaceNode.typeParameters, interfaceNode.heritageClauses, updatedMembers);
|
|
194
|
+
}
|
|
195
|
+
return ts.visitEachChild(node, visit, context);
|
|
196
|
+
};
|
|
197
|
+
return node => ts.visitNode(node, visit);
|
|
198
|
+
};
|
|
199
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
200
|
+
return result.transformed[0];
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @internal
|
|
204
|
+
*
|
|
205
|
+
* for each found enum in the app (i.e. LocalizedTextsEnum), this will also
|
|
206
|
+
* inject below it a `LocalizedTextsEnumValues` const, which can be used
|
|
207
|
+
* at runtime to grab the actual enum values.
|
|
208
|
+
*/
|
|
209
|
+
async addEnumValueExports(dbSourceFile) {
|
|
210
|
+
const enums = await this.getAllEnumValueNames();
|
|
211
|
+
const enumTypes = enums.map(e => e.enumType);
|
|
212
|
+
// @ts-expect-error cannot lock this type down, though implementation is correct
|
|
213
|
+
const transformer = context => {
|
|
214
|
+
const visit = (node) => {
|
|
215
|
+
const typeAlias = this.exportedTypeAliasOrNull(node);
|
|
216
|
+
const isEnum = typeAlias ? enumTypes.includes(typeAlias.name.text) : false;
|
|
217
|
+
if (typeAlias && isEnum) {
|
|
218
|
+
const literals = this.extractStringLiteralTypeNodesFromTypeOrUnion(typeAlias).map(node => node.literal.text);
|
|
219
|
+
if (literals.length > 0) {
|
|
220
|
+
return [
|
|
221
|
+
typeAlias,
|
|
222
|
+
f.createVariableStatement([f.createModifier(ts.SyntaxKind.ExportKeyword)], f.createVariableDeclarationList([
|
|
223
|
+
f.createVariableDeclaration(f.createIdentifier(`${typeAlias.name.text}Values`), undefined, undefined, f.createAsExpression(f.createArrayLiteralExpression(literals.map(literal => f.createStringLiteral(literal)), true // multiLine
|
|
224
|
+
), f.createKeywordTypeNode(ts.SyntaxKind.ConstKeyword))),
|
|
225
|
+
], ts.NodeFlags.Const)),
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return ts.visitEachChild(node, visit, context);
|
|
230
|
+
};
|
|
231
|
+
return node => ts.visitNode(node, visit);
|
|
232
|
+
};
|
|
233
|
+
const result = ts.transform(dbSourceFile, [transformer]);
|
|
234
|
+
return result.transformed[0];
|
|
235
|
+
}
|
|
236
|
+
}
|