@postxl/generator 0.14.0 → 0.15.1
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/generator.js +16 -0
- package/dist/generators/indices/businesslogicindex.generator.d.ts +9 -0
- package/dist/generators/indices/businesslogicindex.generator.js +19 -0
- package/dist/generators/indices/businesslogicmodule.generator.d.ts +9 -0
- package/dist/generators/indices/businesslogicmodule.generator.js +43 -0
- package/dist/generators/indices/businesslogicservice.generator.d.ts +9 -0
- package/dist/generators/indices/businesslogicservice.generator.js +32 -0
- package/dist/generators/indices/datamockmodule.generator.js +1 -1
- package/dist/generators/indices/datamodule.generator.js +22 -21
- package/dist/generators/indices/dataservice.generator.js +1 -1
- package/dist/generators/indices/seed-template-decoder.generator.js +32 -11
- package/dist/generators/models/businesslogic.generator.d.ts +9 -0
- package/dist/generators/models/businesslogic.generator.js +172 -0
- package/dist/generators/models/react.generator/context.generator.js +5 -3
- package/dist/generators/models/react.generator/library.generator.js +1 -1
- package/dist/generators/models/react.generator/lookup.generator.js +1 -1
- package/dist/generators/models/react.generator/modals.generator.d.ts +1 -1
- package/dist/generators/models/react.generator/modals.generator.js +118 -60
- package/dist/generators/models/repository.generator.js +18 -2
- package/dist/generators/models/route.generator.js +6 -6
- package/dist/generators/models/seed.generator.js +5 -2
- package/dist/generators/models/stub.generator.js +11 -9
- package/dist/generators/models/types.generator.js +32 -2
- package/dist/lib/imports.js +12 -3
- package/dist/lib/meta.d.ts +101 -1
- package/dist/lib/meta.js +21 -0
- package/dist/lib/schema/fields.d.ts +5 -1
- package/dist/lib/schema/schema.d.ts +26 -1
- package/dist/lib/schema/schema.js +1 -1
- package/dist/prisma/parse.js +15 -3
- package/package.json +2 -2
|
@@ -24,12 +24,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.generateDeleteModalModelComponent = exports.generateEditModalModelComponent = exports.generateModelCreateModalComponent = void 0;
|
|
27
|
-
const
|
|
28
|
-
const StringUtils = __importStar(require("../../../lib/utils/string"));
|
|
27
|
+
const imports_1 = require("../../../lib/imports");
|
|
29
28
|
const meta_1 = require("../../../lib/meta");
|
|
30
29
|
const fields_1 = require("../../../lib/schema/fields");
|
|
31
|
-
const
|
|
30
|
+
const serializer_1 = require("../../../lib/serializer");
|
|
32
31
|
const types_1 = require("../../../lib/types");
|
|
32
|
+
const StringUtils = __importStar(require("../../../lib/utils/string"));
|
|
33
33
|
/**
|
|
34
34
|
* Utility generator that creates a create modal component for a given model.
|
|
35
35
|
*/
|
|
@@ -37,10 +37,11 @@ function generateModelCreateModalComponent({ model, meta }) {
|
|
|
37
37
|
const { fields } = model;
|
|
38
38
|
const { react: { components: { modals }, }, trpc, } = meta;
|
|
39
39
|
return `
|
|
40
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
40
41
|
${getFormImports({ model, meta })}
|
|
41
42
|
|
|
42
43
|
type CreateInputData = {
|
|
43
|
-
${
|
|
44
|
+
${getCreateFormInputFields({ model })}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/**
|
|
@@ -62,7 +63,7 @@ export const ${modals.createComponentName} = ({ show, onHide }: { show: boolean;
|
|
|
62
63
|
validate={(values) => {
|
|
63
64
|
const errors: FormikErrors<CreateInputData> = {}
|
|
64
65
|
|
|
65
|
-
${getFormikValidationCases({ model
|
|
66
|
+
${getFormikValidationCases({ model })}
|
|
66
67
|
|
|
67
68
|
return errors
|
|
68
69
|
}}
|
|
@@ -72,7 +73,7 @@ export const ${modals.createComponentName} = ({ show, onHide }: { show: boolean;
|
|
|
72
73
|
const res = await toast.promise(
|
|
73
74
|
mutation.mutateAsync(
|
|
74
75
|
{
|
|
75
|
-
${
|
|
76
|
+
${getFormikCreateMutationData({ model })}
|
|
76
77
|
},
|
|
77
78
|
{
|
|
78
79
|
async onSuccess() {
|
|
@@ -95,15 +96,14 @@ export const ${modals.createComponentName} = ({ show, onHide }: { show: boolean;
|
|
|
95
96
|
}
|
|
96
97
|
}}
|
|
97
98
|
>
|
|
98
|
-
{({ isSubmitting,
|
|
99
|
+
{({ isSubmitting, submitForm, isValid }) => (
|
|
99
100
|
<ModalWithActions actions={
|
|
100
101
|
<ConfirmButton
|
|
101
102
|
color="primary"
|
|
102
103
|
label="Create"
|
|
103
104
|
fill="fill"
|
|
104
|
-
onClick={
|
|
105
|
+
onClick={submitForm}
|
|
105
106
|
loading={isSubmitting}
|
|
106
|
-
disabled={!isValid}
|
|
107
107
|
/>
|
|
108
108
|
}>
|
|
109
109
|
${getFormFieldComponents({ model })}
|
|
@@ -124,12 +124,13 @@ function generateEditModalModelComponent({ model, meta }) {
|
|
|
124
124
|
const { fields } = model;
|
|
125
125
|
const { react: { components }, trpc, } = meta;
|
|
126
126
|
return `
|
|
127
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
127
128
|
${getFormImports({ model, meta })}
|
|
128
129
|
|
|
129
130
|
|
|
130
131
|
type EditInputData = {
|
|
131
132
|
${getFormikFieldName(model.idField.name)}: ${model.brandedIdType}
|
|
132
|
-
${
|
|
133
|
+
${getEditFormInputFields({ model })}
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
/**
|
|
@@ -168,7 +169,7 @@ export const ${components.modals.editComponentName} = ({
|
|
|
168
169
|
validate={(values) => {
|
|
169
170
|
const errors: { [K in keyof EditInputData]?: string } = {}
|
|
170
171
|
|
|
171
|
-
${getFormikValidationCases({ model
|
|
172
|
+
${getFormikValidationCases({ model })}
|
|
172
173
|
|
|
173
174
|
return errors
|
|
174
175
|
}}
|
|
@@ -177,7 +178,7 @@ export const ${components.modals.editComponentName} = ({
|
|
|
177
178
|
await toast.promise(
|
|
178
179
|
mutation.mutateAsync(
|
|
179
180
|
{
|
|
180
|
-
${
|
|
181
|
+
${getEditFormikMutationData({ model })}
|
|
181
182
|
},
|
|
182
183
|
{
|
|
183
184
|
async onSuccess() {
|
|
@@ -198,15 +199,14 @@ export const ${components.modals.editComponentName} = ({
|
|
|
198
199
|
}
|
|
199
200
|
}}
|
|
200
201
|
>
|
|
201
|
-
{({ isSubmitting,
|
|
202
|
+
{({ isSubmitting, submitForm }) => (
|
|
202
203
|
<ModalWithActions
|
|
203
204
|
actions={
|
|
204
205
|
<ConfirmButton
|
|
205
206
|
label="Save"
|
|
206
207
|
color="primary"
|
|
207
|
-
onClick={
|
|
208
|
+
onClick={submitForm}
|
|
208
209
|
loading={isSubmitting}
|
|
209
|
-
disabled={!isValid}
|
|
210
210
|
/>
|
|
211
211
|
}
|
|
212
212
|
>
|
|
@@ -230,6 +230,7 @@ function generateDeleteModalModelComponent({ model, meta }) {
|
|
|
230
230
|
from: meta.types.importPath,
|
|
231
231
|
});
|
|
232
232
|
return `
|
|
233
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
233
234
|
import { Formik, FormikErrors } from 'formik'
|
|
234
235
|
import React, { useCallback } from 'react'
|
|
235
236
|
import { toast } from 'react-hot-toast'
|
|
@@ -335,17 +336,86 @@ function getFormImports({ model, meta }) {
|
|
|
335
336
|
${imports.generate()}
|
|
336
337
|
`;
|
|
337
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Returns a string containing TypeScript type definition for the input data of the create mutation.
|
|
341
|
+
*
|
|
342
|
+
* NOTE: This function assumes that fields might also be null because we have no data yet.
|
|
343
|
+
*/
|
|
344
|
+
function getCreateFormInputFields({ model }) {
|
|
345
|
+
const form = new serializer_1.Serializer();
|
|
346
|
+
for (const field of model.fields.values()) {
|
|
347
|
+
switch (field.kind) {
|
|
348
|
+
case 'id':
|
|
349
|
+
continue;
|
|
350
|
+
case 'relation':
|
|
351
|
+
form.append(`${getFormikFieldName(field.name)}: { id: ${field.relationToModel.brandedIdType} } | null`);
|
|
352
|
+
break;
|
|
353
|
+
case 'scalar':
|
|
354
|
+
form.append(`${getFormikFieldName(field.name)}: ${field.typeName} | null`);
|
|
355
|
+
break;
|
|
356
|
+
case 'enum':
|
|
357
|
+
form.append(`${getFormikFieldName(field.name)}: { id: ${field.typeName} } | null`);
|
|
358
|
+
break;
|
|
359
|
+
default:
|
|
360
|
+
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return form.print();
|
|
364
|
+
}
|
|
365
|
+
function getFormikCreateMutationData({ model: { fields } }) {
|
|
366
|
+
return fields
|
|
367
|
+
.map((field) => {
|
|
368
|
+
const formikFieldName = getFormikFieldName(field.name);
|
|
369
|
+
switch (field.kind) {
|
|
370
|
+
case 'id':
|
|
371
|
+
case 'scalar':
|
|
372
|
+
// NOTE: Create methods generate the ID field upon submision.
|
|
373
|
+
if (field.kind === 'id') {
|
|
374
|
+
return '';
|
|
375
|
+
}
|
|
376
|
+
if (field.isRequired) {
|
|
377
|
+
return `
|
|
378
|
+
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
379
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
380
|
+
${field.name}: values.${formikFieldName}!,
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
return `${field.name}: values.${formikFieldName},`;
|
|
384
|
+
case 'relation':
|
|
385
|
+
if (field.isRequired) {
|
|
386
|
+
return `
|
|
387
|
+
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
389
|
+
${field.name}: values.${formikFieldName}!.id,
|
|
390
|
+
`;
|
|
391
|
+
}
|
|
392
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
393
|
+
case 'enum':
|
|
394
|
+
if (field.isRequired) {
|
|
395
|
+
return `
|
|
396
|
+
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
397
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
398
|
+
${field.name}: values.${formikFieldName}!.id,
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
402
|
+
default:
|
|
403
|
+
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
.join('\n');
|
|
407
|
+
}
|
|
338
408
|
/**
|
|
339
409
|
* Returns a string containing the fields of the input data without the ID field.
|
|
340
410
|
*/
|
|
341
|
-
function
|
|
411
|
+
function getEditFormInputFields({ model }) {
|
|
342
412
|
const form = new serializer_1.Serializer();
|
|
343
413
|
for (const field of model.fields.values()) {
|
|
344
414
|
switch (field.kind) {
|
|
345
415
|
case 'id':
|
|
346
416
|
continue;
|
|
347
417
|
case 'relation':
|
|
348
|
-
if (field.isRequired
|
|
418
|
+
if (field.isRequired) {
|
|
349
419
|
form.append(`${getFormikFieldName(field.name)}: { id: ${field.relationToModel.brandedIdType} }`);
|
|
350
420
|
}
|
|
351
421
|
else {
|
|
@@ -353,7 +423,7 @@ function getFormInputFields({ model, nullable }) {
|
|
|
353
423
|
}
|
|
354
424
|
break;
|
|
355
425
|
case 'scalar':
|
|
356
|
-
if (field.isRequired
|
|
426
|
+
if (field.isRequired) {
|
|
357
427
|
form.append(`${getFormikFieldName(field.name)}: ${field.typeName}`);
|
|
358
428
|
}
|
|
359
429
|
else {
|
|
@@ -361,7 +431,7 @@ function getFormInputFields({ model, nullable }) {
|
|
|
361
431
|
}
|
|
362
432
|
break;
|
|
363
433
|
case 'enum':
|
|
364
|
-
if (field.isRequired
|
|
434
|
+
if (field.isRequired) {
|
|
365
435
|
form.append(`${getFormikFieldName(field.name)}: { id: ${field.typeName} }`);
|
|
366
436
|
}
|
|
367
437
|
else {
|
|
@@ -374,6 +444,32 @@ function getFormInputFields({ model, nullable }) {
|
|
|
374
444
|
}
|
|
375
445
|
return form.print();
|
|
376
446
|
}
|
|
447
|
+
function getEditFormikMutationData({ model: { fields } }) {
|
|
448
|
+
return fields
|
|
449
|
+
.map((field) => {
|
|
450
|
+
const formikFieldName = getFormikFieldName(field.name);
|
|
451
|
+
switch (field.kind) {
|
|
452
|
+
case 'id':
|
|
453
|
+
// NOTE: Edit mutation expects the ID field to identify the object.
|
|
454
|
+
// eslint-disable-next-line no-fallthrough
|
|
455
|
+
case 'scalar':
|
|
456
|
+
return `${field.name}: values.${formikFieldName},`;
|
|
457
|
+
case 'relation':
|
|
458
|
+
if (field.isRequired) {
|
|
459
|
+
return `${field.name}: values.${formikFieldName}.id,`;
|
|
460
|
+
}
|
|
461
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
462
|
+
case 'enum':
|
|
463
|
+
if (field.isRequired) {
|
|
464
|
+
return `${field.name}: values.${formikFieldName}.id,`;
|
|
465
|
+
}
|
|
466
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
467
|
+
default:
|
|
468
|
+
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
469
|
+
}
|
|
470
|
+
})
|
|
471
|
+
.join('\n');
|
|
472
|
+
}
|
|
377
473
|
/**
|
|
378
474
|
* Returns a string containing all the components that should appear in the Formik form for this model.
|
|
379
475
|
*/
|
|
@@ -462,11 +558,12 @@ function getFormFieldComponents({ model }) {
|
|
|
462
558
|
/**
|
|
463
559
|
* Returns a string containing a list of checks for Formik validation function.
|
|
464
560
|
*/
|
|
465
|
-
function getFormikValidationCases({ model
|
|
561
|
+
function getFormikValidationCases({ model }) {
|
|
466
562
|
const form = new serializer_1.Serializer();
|
|
467
563
|
for (const field of model.fields.values()) {
|
|
468
|
-
if (
|
|
564
|
+
if (field.kind === 'id') {
|
|
469
565
|
continue;
|
|
566
|
+
}
|
|
470
567
|
const formikFieldName = getFormikFieldName(field.name);
|
|
471
568
|
if (field.isRequired) {
|
|
472
569
|
form.append(`
|
|
@@ -478,45 +575,6 @@ function getFormikValidationCases({ model, includeId }) {
|
|
|
478
575
|
}
|
|
479
576
|
return form.print();
|
|
480
577
|
}
|
|
481
|
-
function getFormikMutationData({ model: { fields }, includeId }) {
|
|
482
|
-
return fields
|
|
483
|
-
.map((field) => {
|
|
484
|
-
const formikFieldName = getFormikFieldName(field.name);
|
|
485
|
-
switch (field.kind) {
|
|
486
|
-
case 'id':
|
|
487
|
-
case 'scalar':
|
|
488
|
-
if (field.kind === 'id' && !includeId) {
|
|
489
|
-
return '';
|
|
490
|
-
}
|
|
491
|
-
if (field.isRequired) {
|
|
492
|
-
return `
|
|
493
|
-
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
494
|
-
${field.name}: values.${formikFieldName}!,
|
|
495
|
-
`;
|
|
496
|
-
}
|
|
497
|
-
return `${field.name}: values.${formikFieldName},`;
|
|
498
|
-
case 'relation':
|
|
499
|
-
if (field.isRequired) {
|
|
500
|
-
return `
|
|
501
|
-
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
502
|
-
${field.name}: values.${formikFieldName}!.id,
|
|
503
|
-
`;
|
|
504
|
-
}
|
|
505
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
506
|
-
case 'enum':
|
|
507
|
-
if (field.isRequired) {
|
|
508
|
-
return `
|
|
509
|
-
// NOTE: We force unwrap values, because we validate values using Formik.
|
|
510
|
-
${field.name}: values.${formikFieldName}!.id,
|
|
511
|
-
`;
|
|
512
|
-
}
|
|
513
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}!.id : null,`;
|
|
514
|
-
default:
|
|
515
|
-
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
516
|
-
}
|
|
517
|
-
})
|
|
518
|
-
.join('\n');
|
|
519
|
-
}
|
|
520
578
|
// NOTE: This function is separated because many parts rely on the name of the field.
|
|
521
579
|
function getFormikFieldName(name) {
|
|
522
580
|
return `formik${StringUtils.toPascalCase(name)}`;
|
|
@@ -60,14 +60,16 @@ function generateRepository({ model, meta }) {
|
|
|
60
60
|
const idIntInitFn = `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`;
|
|
61
61
|
const imports = imports_1.ImportsGenerator.from(meta.data.repoFilePath)
|
|
62
62
|
.addImport({
|
|
63
|
-
items: [model.typeName, model.brandedIdType, meta.types.toBrandedIdTypeFnName],
|
|
63
|
+
items: [model.typeName, model.brandedIdType, ...(isGenerated ? [meta.types.toBrandedIdTypeFnName] : [])],
|
|
64
64
|
from: meta.types.importPath,
|
|
65
65
|
})
|
|
66
|
-
.addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath })
|
|
67
66
|
.addImport({
|
|
68
67
|
items: [(0, types_1.toClassName)('Repository')],
|
|
69
68
|
from: (0, types_1.toFileName)(model.schemaConfig.paths.dataLibPath + 'repository.type'),
|
|
70
69
|
});
|
|
70
|
+
if (!model.attributes.inMemoryOnly) {
|
|
71
|
+
imports.addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath });
|
|
72
|
+
}
|
|
71
73
|
relations.forEach((r) => {
|
|
72
74
|
imports.addImport({
|
|
73
75
|
items: [r.meta.types.brandedIdType],
|
|
@@ -76,8 +78,12 @@ function generateRepository({ model, meta }) {
|
|
|
76
78
|
});
|
|
77
79
|
return `
|
|
78
80
|
import { Injectable, Logger } from '@nestjs/common'
|
|
81
|
+
${model.attributes.inMemoryOnly
|
|
82
|
+
? ''
|
|
83
|
+
: `
|
|
79
84
|
import { DbService, ${model.sourceName} as DbType } from '@${model.schemaConfig.project}/db'
|
|
80
85
|
import { format, pluralize ${indexes.length === 0 ? '' : ', NestedMap'} } from '@pxl/common'
|
|
86
|
+
`}
|
|
81
87
|
|
|
82
88
|
${imports.generate()}
|
|
83
89
|
|
|
@@ -237,6 +243,11 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
237
243
|
${[...model.fields.values()].map((f) => `${f.sourceName}: item.${f.name}`).join(',\n')}
|
|
238
244
|
}
|
|
239
245
|
}
|
|
246
|
+
${model.attributes.inMemoryOnly
|
|
247
|
+
? `
|
|
248
|
+
// Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars`
|
|
250
|
+
: ''}
|
|
240
251
|
public async createWithId(item: Omit<${model.typeName}, '${idField.name}'> & {
|
|
241
252
|
id: ${model.brandedIdType}
|
|
242
253
|
}): Promise<${model.typeName}> {
|
|
@@ -256,6 +267,11 @@ export class ${meta.data.repositoryClassName} implements Repository<
|
|
|
256
267
|
return newItem
|
|
257
268
|
}
|
|
258
269
|
|
|
270
|
+
${model.attributes.inMemoryOnly
|
|
271
|
+
? `
|
|
272
|
+
// Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
|
|
273
|
+
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars`
|
|
274
|
+
: ''}
|
|
259
275
|
public async create(
|
|
260
276
|
item: Omit<${model.typeName}, 'id'>
|
|
261
277
|
): Promise<${model.typeName}> {
|
|
@@ -11,7 +11,7 @@ const imports_1 = require("../../lib/imports");
|
|
|
11
11
|
function generateRoute({ model, meta }) {
|
|
12
12
|
const { idField, defaultField } = model;
|
|
13
13
|
const defaultValueMethod = `
|
|
14
|
-
getDefault: procedure.query(({ ctx }) => ctx.
|
|
14
|
+
getDefault: procedure.query(({ ctx }) => ctx.data.${meta.data.dataServiceName}.defaultValue),
|
|
15
15
|
`;
|
|
16
16
|
const createMethod = getCreateMethod({ model, meta });
|
|
17
17
|
const updateMethod = getUpdateMethod({ model, meta });
|
|
@@ -38,8 +38,8 @@ export const ${meta.trpc.routerName} = router({
|
|
|
38
38
|
|
|
39
39
|
get: procedure
|
|
40
40
|
.input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
|
|
41
|
-
.query(
|
|
42
|
-
getMap: procedure.query(({ ctx }) => ctx.
|
|
41
|
+
.query(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.get(input)),
|
|
42
|
+
getMap: procedure.query(({ ctx }) => ctx.data.${meta.data.dataServiceName}.getAll()),
|
|
43
43
|
|
|
44
44
|
${omit(createMethod, model.attributes.skipCreate)}
|
|
45
45
|
${omit(updateMethod, model.attributes.skipUpdate)}
|
|
@@ -66,7 +66,7 @@ function getCreateMethod({ model: { fields }, meta }) {
|
|
|
66
66
|
create: procedure.input(z.object({
|
|
67
67
|
${parameters}
|
|
68
68
|
}))
|
|
69
|
-
.mutation(({ input, ctx }) => ctx.
|
|
69
|
+
.mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.create(input)),
|
|
70
70
|
`;
|
|
71
71
|
}
|
|
72
72
|
function getUpdateMethod({ model: { fields }, meta }) {
|
|
@@ -79,14 +79,14 @@ function getUpdateMethod({ model: { fields }, meta }) {
|
|
|
79
79
|
${parameters}
|
|
80
80
|
})
|
|
81
81
|
)
|
|
82
|
-
.mutation(({ input, ctx }) => ctx.
|
|
82
|
+
.mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.update(input)),
|
|
83
83
|
`;
|
|
84
84
|
}
|
|
85
85
|
function getDeleteMethod({ idField, meta }) {
|
|
86
86
|
return `
|
|
87
87
|
delete: procedure
|
|
88
88
|
.input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
|
|
89
|
-
.mutation(({ input, ctx }) => ctx.
|
|
89
|
+
.mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.delete(input)),
|
|
90
90
|
`;
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
@@ -99,12 +99,15 @@ function generateFieldDataId({ field, index }) {
|
|
|
99
99
|
const idModelMeta = (0, meta_1.getModelMetadata)({ model: field.model });
|
|
100
100
|
return `${idModelMeta.types.toBrandedIdTypeFnName}(${field.unbrandedTypeName === 'string' ? `'${index}'` : index})`;
|
|
101
101
|
}
|
|
102
|
+
function quoteSingleQuote(str) {
|
|
103
|
+
return str.replace(/'/g, "\\'");
|
|
104
|
+
}
|
|
102
105
|
function generateFieldDataScalar({ field, model, index, exampleMode, }) {
|
|
103
106
|
const { hasExample, example } = getFieldExample({ field, model, index, exampleMode });
|
|
104
107
|
switch (field.typeName) {
|
|
105
108
|
case 'string': {
|
|
106
|
-
if (hasExample) {
|
|
107
|
-
return
|
|
109
|
+
if (hasExample && typeof example === 'string') {
|
|
110
|
+
return `'${quoteSingleQuote(example)}'`;
|
|
108
111
|
}
|
|
109
112
|
const result = generateFieldDataString({ field, model, index });
|
|
110
113
|
if (result === null) {
|
|
@@ -14,13 +14,7 @@ function generateStub({ model, meta }) {
|
|
|
14
14
|
items: [model.typeName, meta.types.toBrandedIdTypeFnName],
|
|
15
15
|
from: meta.types.importPath,
|
|
16
16
|
});
|
|
17
|
-
|
|
18
|
-
const depMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
|
|
19
|
-
imports.addImport({
|
|
20
|
-
items: [relation.relationToModel.typeName, depMeta.types.toBrandedIdTypeFnName],
|
|
21
|
-
from: meta.types.importPath,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
17
|
+
const assignments = getAssignmentStatementModel({ fields, imports });
|
|
24
18
|
return `
|
|
25
19
|
${imports.generate()}
|
|
26
20
|
|
|
@@ -28,7 +22,7 @@ ${imports.generate()}
|
|
|
28
22
|
* Utility object containing default values for all fields in a model.
|
|
29
23
|
*/
|
|
30
24
|
export const ${meta.data.defaultStubConstantName}: ${model.typeName} = {
|
|
31
|
-
${
|
|
25
|
+
${assignments}
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
/**
|
|
@@ -46,7 +40,7 @@ exports.generateStub = generateStub;
|
|
|
46
40
|
/**
|
|
47
41
|
* Return an assignment statement for a model where each field is assigned null or its type's default value.
|
|
48
42
|
*/
|
|
49
|
-
function
|
|
43
|
+
function getAssignmentStatementModel({ fields, imports }) {
|
|
50
44
|
return fields
|
|
51
45
|
.map((f) => {
|
|
52
46
|
if (!f.isRequired) {
|
|
@@ -58,10 +52,18 @@ function getAssigmentStatementModel({ fields }) {
|
|
|
58
52
|
}
|
|
59
53
|
case 'id': {
|
|
60
54
|
const idRefMeta = (0, meta_1.getModelMetadata)({ model: f.model });
|
|
55
|
+
imports.addImport({
|
|
56
|
+
items: [idRefMeta.types.toBrandedIdTypeFnName],
|
|
57
|
+
from: idRefMeta.types.importPath,
|
|
58
|
+
});
|
|
61
59
|
return `${f.name}: ${idRefMeta.types.toBrandedIdTypeFnName}(${(0, fields_1.getDefaultValueForType)(f.unbrandedTypeName)})`;
|
|
62
60
|
}
|
|
63
61
|
case 'relation': {
|
|
64
62
|
const refModelMeta = (0, meta_1.getModelMetadata)({ model: f.relationToModel });
|
|
63
|
+
imports.addImport({
|
|
64
|
+
items: [refModelMeta.types.toBrandedIdTypeFnName],
|
|
65
|
+
from: refModelMeta.types.importPath,
|
|
66
|
+
});
|
|
65
67
|
return `${f.name}: ${refModelMeta.types.toBrandedIdTypeFnName}(${(0, fields_1.getDefaultValueForType)(f.unbrandedTypeName)})`;
|
|
66
68
|
}
|
|
67
69
|
case 'enum': {
|
|
@@ -12,6 +12,7 @@ const types_1 = require("../../lib/types");
|
|
|
12
12
|
function generateModelTypes({ model, meta }) {
|
|
13
13
|
const idField = model.idField;
|
|
14
14
|
const imports = imports_1.ImportsGenerator.from(meta.types.filePath);
|
|
15
|
+
let hasLinkedItems = false;
|
|
15
16
|
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
16
17
|
if (relation.relationToModel.typeName === model.typeName) {
|
|
17
18
|
continue;
|
|
@@ -19,9 +20,10 @@ function generateModelTypes({ model, meta }) {
|
|
|
19
20
|
const refModel = relation.relationToModel;
|
|
20
21
|
const refMeta = (0, meta_1.getModelMetadata)({ model: refModel });
|
|
21
22
|
imports.addImport({
|
|
22
|
-
items: [refMeta.types.toBrandedIdTypeFnName, refModel.brandedIdType],
|
|
23
|
+
items: [refMeta.types.toBrandedIdTypeFnName, refModel.brandedIdType, refMeta.types.typeName],
|
|
23
24
|
from: refMeta.types.filePath,
|
|
24
25
|
});
|
|
26
|
+
hasLinkedItems = true;
|
|
25
27
|
}
|
|
26
28
|
for (const f of (0, fields_1.getEnumFields)(model)) {
|
|
27
29
|
const refEnumMeta = (0, meta_1.getEnumMetadata)({ enumerator: f.enumerator });
|
|
@@ -42,7 +44,7 @@ ${model.description
|
|
|
42
44
|
* ${model.description.split('\n').join('\n * ')}
|
|
43
45
|
*/`
|
|
44
46
|
: ''}
|
|
45
|
-
export type ${
|
|
47
|
+
export type ${meta.types.typeName} = {
|
|
46
48
|
${model.fields
|
|
47
49
|
.map((f) => `
|
|
48
50
|
${getFieldComment(f)}
|
|
@@ -50,6 +52,17 @@ ${f.name}: ${getFieldType(f)}${f.isRequired ? '' : ' | null'}`)
|
|
|
50
52
|
.join('\n')}
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
${!hasLinkedItems
|
|
56
|
+
? ``
|
|
57
|
+
: `
|
|
58
|
+
export type ${meta.types.linkedTypeName} = {
|
|
59
|
+
${model.fields
|
|
60
|
+
.map((f) => `
|
|
61
|
+
${getFieldComment(f)}
|
|
62
|
+
${getLinkedFieldType(f)}${f.isRequired ? '' : ' | null'}`)
|
|
63
|
+
.join('\n')}
|
|
64
|
+
}`}
|
|
65
|
+
|
|
53
66
|
/**
|
|
54
67
|
* Branded Id type that should be used to identify an instance of this model.
|
|
55
68
|
*/
|
|
@@ -112,3 +125,20 @@ function getFieldType(f) {
|
|
|
112
125
|
throw new types_1.ExhaustiveSwitchCheck(f);
|
|
113
126
|
}
|
|
114
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Converts a field to a TypeScript type definition with linked fields.
|
|
130
|
+
*/
|
|
131
|
+
function getLinkedFieldType(f) {
|
|
132
|
+
switch (f.kind) {
|
|
133
|
+
case 'enum':
|
|
134
|
+
return `${f.name}: ${f.typeName}`;
|
|
135
|
+
case 'relation':
|
|
136
|
+
return `${f.relatedModelBacklinkFieldName}: ${f.relationToModel.typeName}`;
|
|
137
|
+
case 'id':
|
|
138
|
+
return `${f.name}: ${f.model.brandedIdType}`;
|
|
139
|
+
case 'scalar':
|
|
140
|
+
return `${f.name}: ${f.typeName}`;
|
|
141
|
+
default:
|
|
142
|
+
throw new types_1.ExhaustiveSwitchCheck(f);
|
|
143
|
+
}
|
|
144
|
+
}
|
package/dist/lib/imports.js
CHANGED
|
@@ -41,15 +41,24 @@ class ImportsGenerator {
|
|
|
41
41
|
* Returns the TypeScript import statement.
|
|
42
42
|
*/
|
|
43
43
|
generate() {
|
|
44
|
-
const
|
|
45
|
-
.sort(([a], [b]) =>
|
|
44
|
+
const statements = Object.entries(this._imports)
|
|
45
|
+
.sort(([a], [b]) => {
|
|
46
|
+
// //in case a or b start with "@", they will be sorted first
|
|
47
|
+
// if (a.startsWith('@') && !b.startsWith('@')) {
|
|
48
|
+
// return -1
|
|
49
|
+
// }
|
|
50
|
+
// if (!a.startsWith('@') && b.startsWith('@')) {
|
|
51
|
+
// return 1
|
|
52
|
+
// }
|
|
53
|
+
return a.localeCompare(b);
|
|
54
|
+
})
|
|
46
55
|
.map(([path, items]) => {
|
|
47
56
|
const alphabeticallySortedItems = Array.from(items).sort((a, b) => a.localeCompare(b));
|
|
48
57
|
// NOTE: You cannot remove imports and we check that there's at least one imported item for every path.
|
|
49
58
|
return `import { ${alphabeticallySortedItems.join(', ')} } from '${path}'`;
|
|
50
59
|
})
|
|
51
60
|
.join('\n');
|
|
52
|
-
return
|
|
61
|
+
return statements;
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
exports.ImportsGenerator = ImportsGenerator;
|