@postxl/generator 0.8.0 → 0.9.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/generators/indices/emptydatabasemigration.generator.js +1 -1
- package/dist/generators/models/repository.generator.js +86 -25
- package/dist/generators/models/seed.generator.js +14 -3
- package/dist/lib/meta.d.ts +9 -2
- package/dist/lib/meta.js +24 -18
- package/dist/lib/schema/schema.d.ts +71 -0
- package/dist/lib/schema/schema.js +16 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ function generateEmptyDatabaseStoredProcedure({ models, meta, }) {
|
|
|
9
9
|
.map((model) => `\t${model.sourceSchemaName !== undefined ? `"${model.sourceSchemaName}".` : ''}"${model.sourceName}"`)
|
|
10
10
|
.join(',\n');
|
|
11
11
|
return `
|
|
12
|
-
|
|
12
|
+
CREATE OR REPLACE PROCEDURE "emptyDatabase"() AS $BODY$ BEGIN IF EXISTS (
|
|
13
13
|
SELECT
|
|
14
14
|
FROM "Configuration"."Config"
|
|
15
15
|
WHERE NOT "isTest"
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateRepository = void 0;
|
|
4
4
|
const fields_1 = require("../../lib/schema/fields");
|
|
5
5
|
const string_1 = require("../../lib/utils/string");
|
|
6
|
+
const schema_1 = require("../../lib/schema/schema");
|
|
7
|
+
const meta_1 = require("../../lib/meta");
|
|
6
8
|
const imports_1 = require("../../lib/imports");
|
|
7
9
|
/**
|
|
8
10
|
* Generates repository data structure for a given model.
|
|
@@ -13,6 +15,9 @@ function generateRepository({ model, meta }) {
|
|
|
13
15
|
const decoder = meta.data.repository.decoderFnName;
|
|
14
16
|
const uniqueStringFields = fields.filter(fields_1.isUniqueStringField);
|
|
15
17
|
const maxLengthStringFields = fields.filter(fields_1.isMaxLengthStringField);
|
|
18
|
+
const relations = fields
|
|
19
|
+
.filter(schema_1.isFieldRelation)
|
|
20
|
+
.map((field) => (Object.assign(Object.assign({}, field), { meta: (0, meta_1.getModelMetadata)({ model: field.relationToModel }), fieldMeta: (0, meta_1.getFieldMetadata)({ field }) })));
|
|
16
21
|
const defaultValueInitFn = `
|
|
17
22
|
if (item.${(_a = model.defaultField) === null || _a === void 0 ? void 0 : _a.name}) {
|
|
18
23
|
if (this.defaultValue) {
|
|
@@ -34,6 +39,12 @@ function generateRepository({ model, meta }) {
|
|
|
34
39
|
from: meta.types.importPath,
|
|
35
40
|
})
|
|
36
41
|
.addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath });
|
|
42
|
+
relations.forEach((r) => {
|
|
43
|
+
imports.addImport({
|
|
44
|
+
items: [r.meta.types.brandedIdType],
|
|
45
|
+
from: r.meta.types.importPath,
|
|
46
|
+
});
|
|
47
|
+
});
|
|
37
48
|
return `
|
|
38
49
|
import { Injectable, Logger } from '@nestjs/common'
|
|
39
50
|
import { DbService, ${model.sourceName} as DbType } from '@${model.schemaConfig.project}/db'
|
|
@@ -51,6 +62,12 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
51
62
|
protected data: Map<${model.brandedIdType}, ${model.typeName}> = new Map()
|
|
52
63
|
protected logger = new Logger(${meta.data.repositoryClassName}.name)
|
|
53
64
|
|
|
65
|
+
${relations
|
|
66
|
+
.map((r) => `
|
|
67
|
+
protected ${r.name}Map: Map<${r.meta.types.brandedIdType}, Set<${model.brandedIdType}>> = new Map()
|
|
68
|
+
`)
|
|
69
|
+
.join('\n')}
|
|
70
|
+
|
|
54
71
|
${model.defaultField ? `public defaultValue!: ${model.typeName}` : ''}
|
|
55
72
|
|
|
56
73
|
${isGenerated
|
|
@@ -169,9 +186,7 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
169
186
|
}),
|
|
170
187
|
)`}
|
|
171
188
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
this.data.set(newItem.id, newItem)
|
|
189
|
+
this.set(newItem)
|
|
175
190
|
return newItem
|
|
176
191
|
}
|
|
177
192
|
|
|
@@ -205,9 +220,7 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
205
220
|
}),
|
|
206
221
|
)`}
|
|
207
222
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.data.set(newItem.id, newItem)
|
|
223
|
+
this.set(newItem)
|
|
211
224
|
return newItem`}
|
|
212
225
|
}
|
|
213
226
|
|
|
@@ -228,9 +241,7 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
228
241
|
})`}
|
|
229
242
|
|
|
230
243
|
for (const item of items) {
|
|
231
|
-
this.
|
|
232
|
-
${isGenerated ? `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }` : ''}
|
|
233
|
-
${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.set(item.${f.name}, item)`).join('\n')}
|
|
244
|
+
this.set(item)
|
|
234
245
|
}
|
|
235
246
|
|
|
236
247
|
return items
|
|
@@ -286,20 +297,9 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
286
297
|
}),
|
|
287
298
|
)`}
|
|
288
299
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return `
|
|
292
|
-
if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
|
|
293
|
-
this.uniqueIds.${f.name}.delete(existingItem.${f.name})
|
|
294
|
-
if (item.${f.name} !== null) {
|
|
295
|
-
this.uniqueIds.${f.name}.set(newItem.${f.name}, newItem)
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
`;
|
|
299
|
-
})
|
|
300
|
-
.join('\n')}
|
|
300
|
+
this.remove(existingItem)
|
|
301
|
+
this.set(newItem)
|
|
301
302
|
|
|
302
|
-
this.data.set(newItem.id, newItem)
|
|
303
303
|
return newItem
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -317,11 +317,28 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
317
317
|
await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
|
|
318
318
|
`}
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this.data.delete(${meta.types.toBrandedIdTypeFnName}(id))
|
|
320
|
+
this.remove(existingItem)
|
|
323
321
|
}
|
|
324
322
|
|
|
323
|
+
${relations
|
|
324
|
+
.map((r) => `
|
|
325
|
+
/**
|
|
326
|
+
* Function to retrieve all ${(0, string_1.pluralize)(model.name)} that are related to a ${r.name}
|
|
327
|
+
*/
|
|
328
|
+
public ${r.fieldMeta.getByForeignKeyMethodFnName}(id: ${r.meta.types.brandedIdType}): ${model.typeName}[] {
|
|
329
|
+
return this.${r.fieldMeta.getByForeignKeyIdsMethodFnName}(id).map((id) => this.get(id) as ${model.typeName})
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Function to retrieve all ${model.brandedIdType}s that are related to a ${r.name}
|
|
334
|
+
*/
|
|
335
|
+
public ${r.fieldMeta.getByForeignKeyIdsMethodFnName}(id: ${r.meta.types.brandedIdType}): ${model.brandedIdType}[] {
|
|
336
|
+
const s = this.${r.name}Map.get(id)
|
|
337
|
+
if (!s) return []
|
|
338
|
+
return Array.from(s)
|
|
339
|
+
}
|
|
340
|
+
`)
|
|
341
|
+
.join('\n')}
|
|
325
342
|
${maxLengthStringFields.map((f) => {
|
|
326
343
|
return `
|
|
327
344
|
/**
|
|
@@ -359,6 +376,50 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
359
376
|
})
|
|
360
377
|
.join('\n\n')}
|
|
361
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Function that adds/updates a given item to the internal data store, indexes, etc.
|
|
381
|
+
*/
|
|
382
|
+
private set(item: ${model.typeName}): void {
|
|
383
|
+
${isGenerated ? `if (item.id > this.currentMaxId) { this.currentMaxId = item.id }` : ''}
|
|
384
|
+
this.data.set(item.id, item)
|
|
385
|
+
${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.set(item.${f.name}, item)`).join('\n')}
|
|
386
|
+
|
|
387
|
+
${relations
|
|
388
|
+
.map((r) => `
|
|
389
|
+
${!r.isRequired ? `if (item.${r.name}) {` : ''}
|
|
390
|
+
let ${r.name}Set = this.${r.name}Map.get(item.${r.name})
|
|
391
|
+
if (!${r.name}Set) {
|
|
392
|
+
${r.name}Set = new Set()
|
|
393
|
+
this.${r.name}Map.set(item.${r.name}, ${r.name}Set)
|
|
394
|
+
}
|
|
395
|
+
${r.name}Set.add(item.id)
|
|
396
|
+
${!r.isRequired ? `}` : ''}
|
|
397
|
+
`)
|
|
398
|
+
.join('\n')}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Function that removes a given item from the internal data store, indexes, etc.
|
|
403
|
+
*/
|
|
404
|
+
private remove(item: ${model.typeName}): void {
|
|
405
|
+
this.data.delete(item.id)
|
|
406
|
+
${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.delete(item.${f.name})`).join('\n')}
|
|
407
|
+
|
|
408
|
+
${relations
|
|
409
|
+
.map((r) => `
|
|
410
|
+
${!r.isRequired ? `if (item.${r.name}) {` : ''}
|
|
411
|
+
const ${r.name}Set = this.${r.name}Map.get(item.${r.name})
|
|
412
|
+
if (${r.name}Set) {
|
|
413
|
+
${r.name}Set.delete(item.id)
|
|
414
|
+
if (${r.name}Set.size === 0) {
|
|
415
|
+
this.${r.name}Map.delete(item.${r.name})
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
${!r.isRequired ? '}' : ''}
|
|
419
|
+
`)
|
|
420
|
+
.join('\n')}
|
|
421
|
+
}
|
|
422
|
+
|
|
362
423
|
/**
|
|
363
424
|
* Utility function that converts a given Database object to a TypeScript model instance
|
|
364
425
|
*/
|
|
@@ -90,7 +90,7 @@ function generateFieldData({ field, model, index, exampleMode, }) {
|
|
|
90
90
|
case 'relation':
|
|
91
91
|
return generateFieldDataRelation({ field, model, index, itemCount: exampleMode.itemCount });
|
|
92
92
|
case 'enum':
|
|
93
|
-
return generateFieldDataEnum({ field });
|
|
93
|
+
return generateFieldDataEnum({ field, exampleMode, index });
|
|
94
94
|
default:
|
|
95
95
|
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
96
96
|
}
|
|
@@ -106,7 +106,12 @@ function generateFieldDataScalar({ field, model, index, exampleMode, }) {
|
|
|
106
106
|
const { hasExample, example } = getFieldExample({ field, model, index, exampleMode });
|
|
107
107
|
switch (field.typeName) {
|
|
108
108
|
case 'string':
|
|
109
|
-
|
|
109
|
+
const result = hasExample ? example : generateFieldDataString({ field, model, index });
|
|
110
|
+
if (result === null)
|
|
111
|
+
return 'null';
|
|
112
|
+
if (result === undefined)
|
|
113
|
+
return 'undefined';
|
|
114
|
+
return `'${quoteSingleQuote(result)}'`;
|
|
110
115
|
case 'number':
|
|
111
116
|
return hasExample ? example : generateFieldDataNumber({ field, model, index });
|
|
112
117
|
case 'boolean':
|
|
@@ -162,7 +167,13 @@ function generateFieldDataRelation({ field, model, index, itemCount, }) {
|
|
|
162
167
|
const brandingFn = refModelMeta.types.toBrandedIdTypeFnName;
|
|
163
168
|
return `${brandingFn}(${field.unbrandedTypeName === 'string' ? `'${referenceId}'` : referenceId})`;
|
|
164
169
|
}
|
|
165
|
-
function generateFieldDataEnum({ field }) {
|
|
170
|
+
function generateFieldDataEnum({ field, exampleMode, index, }) {
|
|
171
|
+
if (exampleMode.mode === 'Tuples') {
|
|
172
|
+
if (field.attributes.examples) {
|
|
173
|
+
const example = field.attributes.examples[(index - 1) % field.attributes.examples.length];
|
|
174
|
+
return `'${example}'`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
166
177
|
return `'${faker_1.faker.helpers.arrayElement(field.enumerator.values)}'`;
|
|
167
178
|
}
|
|
168
179
|
/**
|
package/dist/lib/meta.d.ts
CHANGED
|
@@ -312,11 +312,19 @@ export type ModelMetaData = {
|
|
|
312
312
|
export declare function getModelMetadata({ model }: {
|
|
313
313
|
model: Schema.ModelCore;
|
|
314
314
|
}): ModelMetaData;
|
|
315
|
-
type FieldMetaData = {
|
|
315
|
+
export type FieldMetaData = {
|
|
316
316
|
/**
|
|
317
317
|
* Name of the field as it should appear in the "exposed" properties of the generated type.
|
|
318
318
|
*/
|
|
319
319
|
tsFieldName: Types.VariableName;
|
|
320
|
+
/**
|
|
321
|
+
* The name of the method that should be used to get all child objects for a given item, e.g. `getItemsForAggregation`.
|
|
322
|
+
*/
|
|
323
|
+
getByForeignKeyMethodFnName: Types.Function;
|
|
324
|
+
/**
|
|
325
|
+
* The name of the method that should be used to get all child Ids for a given item, e.g. `getIdsForAggregation`.
|
|
326
|
+
*/
|
|
327
|
+
getByForeignKeyIdsMethodFnName: Types.Function;
|
|
320
328
|
};
|
|
321
329
|
/**
|
|
322
330
|
* A collection of hardcoded values shared across multiple generators related to the given field in the model.
|
|
@@ -372,4 +380,3 @@ export type EnumMetaData = {
|
|
|
372
380
|
export declare function getEnumMetadata({ enumerator: { name, schemaConfig: config }, }: {
|
|
373
381
|
enumerator: Schema.Enum;
|
|
374
382
|
}): EnumMetaData;
|
|
375
|
-
export {};
|
package/dist/lib/meta.js
CHANGED
|
@@ -153,24 +153,30 @@ exports.getModelMetadata = getModelMetadata;
|
|
|
153
153
|
* A collection of hardcoded values shared across multiple generators related to the given field in the model.
|
|
154
154
|
*/
|
|
155
155
|
function getFieldMetadata({ field }) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
156
|
+
const PascalCase = (0, string_1.toPascalCase)(field.name);
|
|
157
|
+
return {
|
|
158
|
+
tsFieldName: Types.toVariableName((0, string_1.toCamelCase)(field.name)),
|
|
159
|
+
getByForeignKeyMethodFnName: Types.toFunction(`get${PascalCase}Items`),
|
|
160
|
+
getByForeignKeyIdsMethodFnName: Types.toFunction(`get${PascalCase}Ids`),
|
|
161
|
+
};
|
|
162
|
+
// switch (field.kind) {
|
|
163
|
+
// case 'id':
|
|
164
|
+
// return {
|
|
165
|
+
// tsFieldName: Types.toVariableName(toCamelCase(field.name)),
|
|
166
|
+
// }
|
|
167
|
+
// case 'enum':
|
|
168
|
+
// return {
|
|
169
|
+
// tsFieldName: Types.toVariableName(toCamelCase(field.name)),
|
|
170
|
+
// }
|
|
171
|
+
// case 'relation':
|
|
172
|
+
// return {
|
|
173
|
+
// tsFieldName: Types.toVariableName(toCamelCase(field.name)),
|
|
174
|
+
// }
|
|
175
|
+
// case 'scalar':
|
|
176
|
+
// return {
|
|
177
|
+
// tsFieldName: Types.toVariableName(toCamelCase(field.name)),
|
|
178
|
+
// }
|
|
179
|
+
// }
|
|
174
180
|
}
|
|
175
181
|
exports.getFieldMetadata = getFieldMetadata;
|
|
176
182
|
/**
|
|
@@ -246,6 +246,36 @@ export type FieldId = Prettify<Omit<FieldCore, 'name'> & {
|
|
|
246
246
|
*/
|
|
247
247
|
unbrandedTypeName: Types.TypeName;
|
|
248
248
|
}>;
|
|
249
|
+
/**
|
|
250
|
+
* Utility function to check if a field is an id field.
|
|
251
|
+
*/
|
|
252
|
+
export declare const isFieldId: (field: Field) => field is Prettify<Omit<FieldCore, "name"> & {
|
|
253
|
+
kind: 'id';
|
|
254
|
+
/**
|
|
255
|
+
* Name of the field and variable (e.g. id)
|
|
256
|
+
*/
|
|
257
|
+
name: string;
|
|
258
|
+
/**
|
|
259
|
+
* Model that this ID field identifies.
|
|
260
|
+
*/
|
|
261
|
+
model: ModelCore;
|
|
262
|
+
/**
|
|
263
|
+
* If true, each element is unique.
|
|
264
|
+
* Note: This is ensured by the repository/database
|
|
265
|
+
*/
|
|
266
|
+
isUnique: boolean;
|
|
267
|
+
/**
|
|
268
|
+
* True if the id field is an autoIncremented field.
|
|
269
|
+
*/
|
|
270
|
+
isGenerated: boolean;
|
|
271
|
+
/**
|
|
272
|
+
* Name of the unbranded TypeScript type of the id field, e.g. string
|
|
273
|
+
*
|
|
274
|
+
* Note: This should only be used in the type generator - all other generators
|
|
275
|
+
* should use the branded type "typeName"!
|
|
276
|
+
*/
|
|
277
|
+
unbrandedTypeName: Types.TypeName;
|
|
278
|
+
}>;
|
|
249
279
|
/**
|
|
250
280
|
* A relation field, representing a "to-relation" in the schema.
|
|
251
281
|
*/
|
|
@@ -269,6 +299,29 @@ export type FieldRelation = Prettify<Omit<FieldCore, 'name'> & {
|
|
|
269
299
|
*/
|
|
270
300
|
relationToModel: ModelCore;
|
|
271
301
|
}>;
|
|
302
|
+
/**
|
|
303
|
+
* Utility function to check if a field is an relation field.
|
|
304
|
+
*/
|
|
305
|
+
export declare const isFieldRelation: (field: Field) => field is Prettify<Omit<FieldCore, "name"> & {
|
|
306
|
+
kind: 'relation';
|
|
307
|
+
/**
|
|
308
|
+
* Name of the field and variable (e.g. aggregationId)
|
|
309
|
+
*/
|
|
310
|
+
name: string;
|
|
311
|
+
/**
|
|
312
|
+
* Name of the unbranded TypeScript type of the field, e.g. string
|
|
313
|
+
*
|
|
314
|
+
* Note: This should only be used in the type generator - all other generators
|
|
315
|
+
* should use the branded type "typeName"!
|
|
316
|
+
*
|
|
317
|
+
* @internal
|
|
318
|
+
*/
|
|
319
|
+
unbrandedTypeName: Types.TypeName;
|
|
320
|
+
/**
|
|
321
|
+
* The referenced model
|
|
322
|
+
*/
|
|
323
|
+
relationToModel: ModelCore;
|
|
324
|
+
}>;
|
|
272
325
|
/**
|
|
273
326
|
* An enum field, e.g. a field that can only have a value of a specific enum.
|
|
274
327
|
*/
|
|
@@ -287,6 +340,24 @@ export type FieldEnum = Prettify<Omit<FieldCore, 'name'> & {
|
|
|
287
340
|
*/
|
|
288
341
|
enumerator: Enum;
|
|
289
342
|
}>;
|
|
343
|
+
/**
|
|
344
|
+
* Utility function to check if a field is an enumid field.
|
|
345
|
+
*/
|
|
346
|
+
export declare const isFieldEnum: (field: Field) => field is Prettify<Omit<FieldCore, "name"> & {
|
|
347
|
+
kind: 'enum';
|
|
348
|
+
/**
|
|
349
|
+
* Name of the field and variable (e.g. language)
|
|
350
|
+
*/
|
|
351
|
+
name: string;
|
|
352
|
+
/**
|
|
353
|
+
* Name of the TypeScript type of the field, e.g. Language
|
|
354
|
+
*/
|
|
355
|
+
typeName: Types.TypeName;
|
|
356
|
+
/**
|
|
357
|
+
* Enum definition of the field.
|
|
358
|
+
*/
|
|
359
|
+
enumerator: Enum;
|
|
360
|
+
}>;
|
|
290
361
|
/**
|
|
291
362
|
* Definition of an enum, consisting of a name and a list of enum members.
|
|
292
363
|
*/
|
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFieldEnum = exports.isFieldRelation = exports.isFieldId = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Utility function to check if a field is an id field.
|
|
6
|
+
*/
|
|
7
|
+
const isFieldId = (field) => field.kind === 'id';
|
|
8
|
+
exports.isFieldId = isFieldId;
|
|
9
|
+
/**
|
|
10
|
+
* Utility function to check if a field is an relation field.
|
|
11
|
+
*/
|
|
12
|
+
const isFieldRelation = (field) => field.kind === 'relation';
|
|
13
|
+
exports.isFieldRelation = isFieldRelation;
|
|
14
|
+
/**
|
|
15
|
+
* Utility function to check if a field is an enumid field.
|
|
16
|
+
*/
|
|
17
|
+
const isFieldEnum = (field) => field.kind === 'enum';
|
|
18
|
+
exports.isFieldEnum = isFieldEnum;
|