@postxl/generator 0.55.0 → 0.56.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 +1 -1
- package/dist/generators/indices/businesslogic-actiontypes.generator.js +18 -7
- package/dist/generators/indices/businesslogic-update-index.generator.js +3 -1
- package/dist/generators/indices/dispatcher-service.generator.js +2 -2
- package/dist/generators/indices/importexport-import-service.generator.js +2 -2
- package/dist/generators/indices/seed-migration.generator.js +9 -7
- package/dist/generators/indices/testdata-service.generator.js +9 -3
- package/dist/generators/models/businesslogic-update.generator.js +162 -218
- package/dist/generators/models/businesslogic-view.generator.js +40 -1
- package/dist/generators/models/route.generator.js +28 -31
- package/dist/generators/models/types.generator.js +10 -99
- package/dist/lib/exports.d.ts +4 -0
- package/dist/lib/exports.js +38 -8
- package/dist/lib/meta.d.ts +15 -52
- package/dist/lib/meta.js +6 -15
- package/dist/lib/schema/types.d.ts +5 -5
- package/dist/lib/utils/jsdoc.d.ts +5 -0
- package/dist/lib/utils/jsdoc.js +22 -1
- package/package.json +1 -1
package/dist/generator.js
CHANGED
|
@@ -221,7 +221,7 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
|
|
|
221
221
|
generated.write(`/${meta.importExport.decoder.fullDecoderFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoder)({ models, meta }));
|
|
222
222
|
generated.write(`/${meta.importExport.converterFunctions.filePath}.ts`, (0, importexport_convert_import_functions_generator_1.generateImportExportConvertImportFunctions)({ models, meta }));
|
|
223
223
|
// Actions
|
|
224
|
-
generated.write(`/${meta.actions.
|
|
224
|
+
generated.write(`/${meta.actions.dispatcher.filePath}.ts`, (0, dispatcher_service_generator_1.generateActionsDispatcherService)({ models, meta }));
|
|
225
225
|
// Business Logic
|
|
226
226
|
generated.write(`/${meta.businessLogic.view.indexFilePath}.ts`, (0, businesslogic_view_index_generator_1.generateBusinessLogicViewIndex)({ models, meta }));
|
|
227
227
|
generated.write(`/${meta.businessLogic.view.moduleFilePath}.ts`, (0, businesslogic_view_module_generator_1.generateBusinessLogicViewModule)({ models, meta }));
|
|
@@ -3,27 +3,38 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateBusinessLogicActionTypes = void 0;
|
|
4
4
|
const imports_1 = require("../../lib/imports");
|
|
5
5
|
const meta_1 = require("../../lib/meta");
|
|
6
|
+
const types_1 = require("../../lib/schema/types");
|
|
7
|
+
const file_1 = require("../../lib/utils/file");
|
|
6
8
|
/**
|
|
7
9
|
* Generates the action types for the BusinessLogicModule.
|
|
8
10
|
*/
|
|
9
11
|
function generateBusinessLogicActionTypes({ models, meta }) {
|
|
10
|
-
const imports = imports_1.ImportsGenerator.from(meta.businessLogic.update.actionTypesFilePath)
|
|
12
|
+
const imports = imports_1.ImportsGenerator.from(meta.businessLogic.update.actionTypesFilePath).addImport({
|
|
13
|
+
from: meta.actions.importPath,
|
|
14
|
+
items: [(0, types_1.toTypeName)('ActionDefPayload'), (0, types_1.toTypeName)('ActionDefResult')],
|
|
15
|
+
});
|
|
16
|
+
const updateServiceImports = [];
|
|
11
17
|
const modelNameTypeElements = [];
|
|
12
18
|
const actionsTypeElements = [];
|
|
13
19
|
const actionResultTypeElements = [];
|
|
14
20
|
for (const model of models) {
|
|
15
21
|
const modelMeta = (0, meta_1.getModelMetadata)({ model });
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
const className = modelMeta.businessLogic.update.serviceClassName;
|
|
23
|
+
const scope = modelMeta.businessLogic.update.actionModelDiscriminantName;
|
|
24
|
+
modelNameTypeElements.push(`'${scope}'`);
|
|
25
|
+
const relativeImportPath = (0, file_1.getRelativePath)({
|
|
26
|
+
from: meta.businessLogic.update.actionTypesFilePath,
|
|
27
|
+
to: modelMeta.businessLogic.update.serviceFilePath,
|
|
19
28
|
});
|
|
20
|
-
|
|
21
|
-
actionsTypeElements.push(
|
|
22
|
-
actionResultTypeElements.push(`${
|
|
29
|
+
updateServiceImports.push(`import type * as ${className} from '${relativeImportPath}'`);
|
|
30
|
+
actionsTypeElements.push(`ActionDefPayload<${className}.Scope, ${className}.Actions>`);
|
|
31
|
+
actionResultTypeElements.push(`${scope}: ActionDefResult<${className}.Actions>`);
|
|
23
32
|
}
|
|
24
33
|
return /* ts */ `
|
|
25
34
|
${imports.generate()}
|
|
26
35
|
|
|
36
|
+
${updateServiceImports.join('\n')}
|
|
37
|
+
|
|
27
38
|
/**
|
|
28
39
|
* Used by any of the default actions (create, update, delete) to identify the model.
|
|
29
40
|
*/
|
|
@@ -13,7 +13,9 @@ function generateBusinessLogicUpdateIndex({ models, meta }) {
|
|
|
13
13
|
exports.exportEverythingFromPath(meta.businessLogic.update.actionTypesFilePath);
|
|
14
14
|
for (const model of models) {
|
|
15
15
|
const meta = (0, meta_1.getModelMetadata)({ model });
|
|
16
|
-
exports.
|
|
16
|
+
exports.exportSelectionFromPath(meta.businessLogic.update.serviceFilePath, [
|
|
17
|
+
meta.businessLogic.update.serviceClassName,
|
|
18
|
+
]);
|
|
17
19
|
}
|
|
18
20
|
return exports.generate();
|
|
19
21
|
}
|
|
@@ -8,7 +8,7 @@ const types_1 = require("../../lib/schema/types");
|
|
|
8
8
|
* Generates the action dispatcher service.
|
|
9
9
|
*/
|
|
10
10
|
function generateActionsDispatcherService({ models, meta }) {
|
|
11
|
-
const imports = imports_1.ImportsGenerator.from(meta.actions.
|
|
11
|
+
const imports = imports_1.ImportsGenerator.from(meta.actions.dispatcher.filePath).addImports({
|
|
12
12
|
[meta.seed.importPath]: [meta.seed.serviceClassName],
|
|
13
13
|
[meta.importExport.importPath]: [meta.importExport.importService.name],
|
|
14
14
|
[meta.businessLogic.update.importPath]: [meta.businessLogic.update.serviceClassName],
|
|
@@ -32,7 +32,7 @@ import { Action, ResultOfAction } from './actions.types'
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@Injectable()
|
|
35
|
-
export class ${meta.actions.
|
|
35
|
+
export class ${meta.actions.dispatcher.class} {
|
|
36
36
|
|
|
37
37
|
// As SeedModule and ImportExportModule get instantiated after the ActionsModule, we use this hack to avoid a circular dependency:
|
|
38
38
|
// we set the seedService and the importService to value and then overwrite it in the services' constructors.
|
|
@@ -22,7 +22,7 @@ function generateImportExportImportService({ models, meta }) {
|
|
|
22
22
|
(0, types_1.toAnnotatedTypeName)(dto.idType),
|
|
23
23
|
(0, types_1.toAnnotatedTypeName)(dto.update),
|
|
24
24
|
],
|
|
25
|
-
[meta.actions.importPath]: [meta.actions.actionExecution.interface, meta.actions.
|
|
25
|
+
[meta.actions.importPath]: [meta.actions.actionExecution.interface, meta.actions.dispatcher.class],
|
|
26
26
|
[types.filePath]: [
|
|
27
27
|
(0, types_1.toAnnotatedTypeName)(delta_Fields),
|
|
28
28
|
(0, types_1.toAnnotatedTypeName)(delta_Model.type),
|
|
@@ -61,7 +61,7 @@ type Delta_Result<Model extends ${dto.genericModel}<ID>, ID extends ${dto.idType
|
|
|
61
61
|
export class ${meta.importExport.importService.name} {
|
|
62
62
|
constructor(
|
|
63
63
|
private readonly data: ${meta.data.dataService.class},
|
|
64
|
-
private readonly dispatcher: ${meta.actions.
|
|
64
|
+
private readonly dispatcher: ${meta.actions.dispatcher.class},
|
|
65
65
|
) {
|
|
66
66
|
// We have a circular dependency between DispatcherService and ${meta.importExport.importService.name}.
|
|
67
67
|
// In order to avoid trouble, instead of using Nest's forwardRef(), we inject the dispatcher service here
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateSeedMigration = void 0;
|
|
4
4
|
const imports_1 = require("../../lib/imports");
|
|
5
5
|
const meta_1 = require("../../lib/meta");
|
|
6
|
+
const types_1 = require("../../lib/schema/types");
|
|
6
7
|
/**
|
|
7
8
|
* Generates the initial migration based on the generated seed data.
|
|
8
9
|
*/
|
|
@@ -14,21 +15,22 @@ function generateSeedMigration({ models, meta }) {
|
|
|
14
15
|
imports.addImports({
|
|
15
16
|
[modelMeta.seed.filePath]: [modelMeta.seed.constantName],
|
|
16
17
|
[meta.importExport.importPath]: [meta.importExport.converterFunctions.importedDataToBulkMutations],
|
|
18
|
+
[meta.seed.importPath]: [(0, types_1.toTypeName)('Action_Seed_Data')],
|
|
17
19
|
});
|
|
18
20
|
modelTypes.push(`${modelMeta.seed.constantName}`);
|
|
19
21
|
}
|
|
20
22
|
return /* ts */ `
|
|
21
|
-
import { createActionSeedData } from '${meta.seed.importPath}'
|
|
22
23
|
${imports.generate()}
|
|
23
24
|
|
|
24
|
-
export const MIGRATION_001_BASEDATA =
|
|
25
|
+
export const MIGRATION_001_BASEDATA: Action_Seed_Data = {
|
|
26
|
+
scope: 'seed',
|
|
27
|
+
type: 'data',
|
|
25
28
|
name: 'Base data',
|
|
26
29
|
order: 1,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
30
|
+
payload: ${meta.importExport.converterFunctions.importedDataToBulkMutations}({
|
|
31
|
+
${modelTypes.join(',\n')}
|
|
32
|
+
}),
|
|
33
|
+
}
|
|
32
34
|
`;
|
|
33
35
|
}
|
|
34
36
|
exports.generateSeedMigration = generateSeedMigration;
|
|
@@ -16,7 +16,6 @@ function generateTestDataService({ models, meta: schemaMeta, }) {
|
|
|
16
16
|
(0, types_1.toAnnotatedTypeName)(schemaMeta.data.dataMockDataType),
|
|
17
17
|
],
|
|
18
18
|
[schemaMeta.importExport.importPath]: [schemaMeta.importExport.converterFunctions.mockDataToBulkMutations],
|
|
19
|
-
[schemaMeta.seed.importPath]: [(0, types_1.toClassName)('createActionSeedData')],
|
|
20
19
|
});
|
|
21
20
|
const reInitCalls = [];
|
|
22
21
|
const modelMetas = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
|
|
@@ -57,12 +56,19 @@ export class TestDataService {
|
|
|
57
56
|
this.logger.log('✍ Setting test data')
|
|
58
57
|
|
|
59
58
|
const mockData = ${schemaMeta.importExport.converterFunctions.mockDataToBulkMutations}(data)
|
|
60
|
-
|
|
59
|
+
|
|
61
60
|
const actionExecution = await this.actionExecutionFactory.create({
|
|
62
|
-
action
|
|
61
|
+
action: {
|
|
62
|
+
scope: 'seed',
|
|
63
|
+
type: 'data',
|
|
64
|
+
name: 'E2E',
|
|
65
|
+
order: 0,
|
|
66
|
+
payload: []
|
|
67
|
+
},
|
|
63
68
|
dbService: this.db,
|
|
64
69
|
user: this.dataService.users.rootUser,
|
|
65
70
|
})
|
|
71
|
+
|
|
66
72
|
try {
|
|
67
73
|
${reInitCalls.join('\n')}
|
|
68
74
|
|
|
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateModelBusinessLogicUpdate = void 0;
|
|
4
4
|
const imports_1 = require("../../lib/imports");
|
|
5
5
|
const meta_1 = require("../../lib/meta");
|
|
6
|
+
const fields_1 = require("../../lib/schema/fields");
|
|
6
7
|
const types_1 = require("../../lib/schema/types");
|
|
8
|
+
const zod_1 = require("../../lib/schema/zod");
|
|
7
9
|
const jsdoc_1 = require("../../lib/utils/jsdoc");
|
|
8
|
-
const repository_generator_1 = require("./repository.generator");
|
|
9
10
|
/**
|
|
10
11
|
* Generates update business logic for a given model.
|
|
11
12
|
* The update logic handles all Create/Update/Delete/Upsert operations. See template's readme for more info.
|
|
@@ -15,239 +16,182 @@ function generateModelBusinessLogicUpdate({ model, meta }) {
|
|
|
15
16
|
const imports = imports_1.ImportsGenerator.from(meta.businessLogic.update.serviceFilePath);
|
|
16
17
|
imports.addImports({
|
|
17
18
|
[meta.data.importPath]: meta.data.repository.className,
|
|
18
|
-
[
|
|
19
|
-
|
|
19
|
+
[meta.types.importPath]: [
|
|
20
|
+
(0, types_1.toAnnotatedTypeName)(model.brandedIdType),
|
|
21
|
+
(0, types_1.toAnnotatedTypeName)(meta.types.typeName),
|
|
22
|
+
meta.types.toBrandedIdTypeFnName,
|
|
23
|
+
],
|
|
20
24
|
[meta.businessLogic.view.serviceFilePath]: [meta.businessLogic.view.serviceClassName],
|
|
25
|
+
[schemaMeta.actions.importPath]: [
|
|
26
|
+
schemaMeta.actions.actionExecution.interface,
|
|
27
|
+
schemaMeta.actions.dispatcher.interface,
|
|
28
|
+
schemaMeta.actions.dispatcher.actionMethod,
|
|
29
|
+
],
|
|
21
30
|
[schemaMeta.businessLogic.update.serviceFilePath]: schemaMeta.businessLogic.update.serviceClassName,
|
|
22
31
|
[schemaMeta.businessLogic.view.serviceFilePath]: schemaMeta.businessLogic.view.serviceClassName,
|
|
23
|
-
[meta.data.importPath]: [meta.data.repository.className],
|
|
24
32
|
});
|
|
33
|
+
const { dispatcher } = schemaMeta.actions;
|
|
34
|
+
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
35
|
+
// NOTE: We add branded id type and type name imports only for foreign models.
|
|
36
|
+
if (relation.relationToModel.typeName === model.typeName) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const refMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
|
|
40
|
+
imports.addImport({
|
|
41
|
+
items: [refMeta.types.toBrandedIdTypeFnName],
|
|
42
|
+
from: refMeta.types.filePath,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
25
45
|
/**
|
|
26
46
|
* The name of the variable that holds the repository instance for the current model
|
|
27
47
|
* (e.g. when we generate business logic service for Aggregation, the AggregationRepository
|
|
28
48
|
* would be referenced using `this.data` variable).
|
|
29
49
|
*/
|
|
30
50
|
const modelRepositoryVariableName = meta.businessLogic.dataRepositoryVariableName;
|
|
31
|
-
/**
|
|
32
|
-
* The name of the variable that holds the central business logic service instance.
|
|
33
|
-
* Instead of injecting a repository instance for each model, we inject this single instance
|
|
34
|
-
* which then provides access to all models' business logic.
|
|
35
|
-
*/
|
|
36
|
-
const updateServiceClassName = 'updateService';
|
|
37
|
-
const viewServiceClassName = 'viewService';
|
|
38
|
-
const actionBlocks = generateActionsBuildingBlocks({ model, meta });
|
|
39
|
-
imports.addImport({ from: meta.types.importPath, items: actionBlocks.importTypes });
|
|
40
51
|
const constructorParameters = [
|
|
41
52
|
`public readonly ${modelRepositoryVariableName}: ${meta.data.repository.className}`,
|
|
42
|
-
`@Inject(forwardRef(() => ${schemaMeta.businessLogic.update.serviceClassName})) private readonly
|
|
43
|
-
`@Inject(forwardRef(() => ${schemaMeta.businessLogic.view.serviceClassName})) private readonly
|
|
53
|
+
`@Inject(forwardRef(() => ${schemaMeta.businessLogic.update.serviceClassName})) private readonly updateService: ${schemaMeta.businessLogic.update.serviceClassName}`,
|
|
54
|
+
`@Inject(forwardRef(() => ${schemaMeta.businessLogic.view.serviceClassName})) private readonly viewService: ${schemaMeta.businessLogic.view.serviceClassName}`,
|
|
44
55
|
];
|
|
45
|
-
const
|
|
56
|
+
const { zodCreateObject, zodUpdateObject, zodUpsertObject } = meta.businessLogic.update;
|
|
46
57
|
return /* ts */ `
|
|
47
|
-
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
48
|
-
import { ExhaustiveSwitchCheck } from '@backend/common'
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
exports.generateModelBusinessLogicUpdate = generateModelBusinessLogicUpdate;
|
|
132
|
-
function generateActionsBuildingBlocks({ model, meta }) {
|
|
133
|
-
const actionTypeDefinition = [];
|
|
134
|
-
const resultMap = [];
|
|
135
|
-
const typeDefinitionWithCreateFunction = [];
|
|
136
|
-
const importTypes = [];
|
|
137
|
-
const dispatcher = [];
|
|
138
|
-
const schemaMeta = (0, meta_1.getSchemaMetadata)({ config: model.schemaConfig });
|
|
139
|
-
const actionDefinitions = prepareActionDefinitions({ model, meta });
|
|
140
|
-
for (const [type, config] of Object.entries(actionDefinitions)) {
|
|
141
|
-
const def = generateAction({ model, meta, type: type, config });
|
|
142
|
-
actionTypeDefinition.push(def.actionName),
|
|
143
|
-
resultMap.push(def.resultMap),
|
|
144
|
-
typeDefinitionWithCreateFunction.push(def.typeDefinition, def.createFunction, ''),
|
|
145
|
-
importTypes.push(...def.importTypes),
|
|
146
|
-
dispatcher.push(def.dispatchCaseExpression);
|
|
58
|
+
import { Inject, Injectable, forwardRef } from '@nestjs/common'
|
|
59
|
+
import { ExhaustiveSwitchCheck, UnionOmit } from '@backend/common'
|
|
60
|
+
import { z } from 'zod'
|
|
61
|
+
|
|
62
|
+
${imports.generate()}
|
|
63
|
+
|
|
64
|
+
export type Scope = "${meta.actions.actionScopeConstType}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Zod decoder for validating the create input of a ${meta.userFriendlyName}.
|
|
70
|
+
*/
|
|
71
|
+
export const ${zodCreateObject} = z.object({
|
|
72
|
+
${model.fields
|
|
73
|
+
.filter((f) => !f.attributes.isReadonly)
|
|
74
|
+
.map((f) => `${f.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field: f })}`)
|
|
75
|
+
.join(',')}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Zod decoder for validating the update input of a ${meta.userFriendlyName} .
|
|
80
|
+
*/
|
|
81
|
+
export const ${zodUpdateObject} = z.object({
|
|
82
|
+
${model.fields
|
|
83
|
+
.filter((f) => !f.attributes.isReadonly || f.kind === 'id')
|
|
84
|
+
.map((f) => `${f.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field: f, allowAnyOptionalField: f.kind !== 'id' })}`)
|
|
85
|
+
.join(',')}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Zod decoder for validating the upsert input of a ${meta.userFriendlyName} .
|
|
90
|
+
*/
|
|
91
|
+
export const ${zodUpsertObject} = z.union([${zodUpdateObject}, ${zodCreateObject}])
|
|
92
|
+
|
|
93
|
+
export type Actions = {
|
|
94
|
+
${(0, jsdoc_1.toJsDocComment)([`Creates a new ${meta.userFriendlyName} and returns it.`])}
|
|
95
|
+
create: {
|
|
96
|
+
payload: z.infer<typeof ${zodCreateObject}>
|
|
97
|
+
result: ${meta.types.typeName}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
${(0, jsdoc_1.toJsDocComment)([`Creates multiple new ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
101
|
+
createMany: {
|
|
102
|
+
payload: z.infer<typeof ${zodCreateObject}>[]
|
|
103
|
+
result: ${meta.types.typeName}[]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
${(0, jsdoc_1.toJsDocComment)([`Updates a ${meta.userFriendlyName} and returns it.`])}
|
|
107
|
+
update: {
|
|
108
|
+
payload: z.infer<typeof ${zodUpdateObject}>
|
|
109
|
+
result: ${meta.types.typeName}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
${(0, jsdoc_1.toJsDocComment)([`Updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
113
|
+
updateMany: {
|
|
114
|
+
payload: z.infer<typeof ${zodUpdateObject}>[]
|
|
115
|
+
result: ${meta.types.typeName}[]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
${(0, jsdoc_1.toJsDocComment)([`Creates or updates a ${meta.userFriendlyName} and returns it.`])}
|
|
119
|
+
upsert: {
|
|
120
|
+
payload: z.infer<typeof ${zodUpsertObject}>
|
|
121
|
+
result: ${meta.types.typeName}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
${(0, jsdoc_1.toJsDocComment)([`Creates or updates multiple ${meta.userFriendlyNamePlural} and returns them.`])}
|
|
125
|
+
upsertMany: {
|
|
126
|
+
payload: z.infer<typeof ${zodUpsertObject}>[]
|
|
127
|
+
result: ${meta.types.typeName}[]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
${(0, jsdoc_1.toJsDocComment)([`Deletes a ${meta.userFriendlyName} and returns its id.`])}
|
|
131
|
+
delete: {
|
|
132
|
+
payload: ${model.brandedIdType}
|
|
133
|
+
result: ${model.brandedIdType}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
${(0, jsdoc_1.toJsDocComment)([`Deletes multiple ${meta.userFriendlyNamePlural} and returns their ids.`])}
|
|
137
|
+
deleteMany: {
|
|
138
|
+
payload: ${model.brandedIdType}[]
|
|
139
|
+
result: ${model.brandedIdType}[]
|
|
140
|
+
}
|
|
147
141
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
142
|
+
|
|
143
|
+
@Injectable()
|
|
144
|
+
export class ${meta.businessLogic.update.serviceClassName} implements ${dispatcher.interface}<Scope, Actions> {
|
|
145
|
+
/**
|
|
146
|
+
* Instance of the ${meta.userFriendlyName} view service for convenience.
|
|
147
|
+
*/
|
|
148
|
+
private view: ${meta.businessLogic.view.serviceClassName}
|
|
149
|
+
|
|
150
|
+
constructor(${constructorParameters.join(',\n')}) {
|
|
151
|
+
this.view = this.viewService.${meta.businessLogic.view.serviceVariableName}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Dispatches an action to the appropriate method of the repository.
|
|
156
|
+
*/
|
|
157
|
+
public async dispatch<A extends ${dispatcher.actionMethod}<Scope, Actions>>({
|
|
158
|
+
action,
|
|
159
|
+
execution,
|
|
160
|
+
}: {
|
|
161
|
+
action: UnionOmit<A, 'result'>
|
|
162
|
+
execution: ${schemaMeta.actions.actionExecution.interface}
|
|
163
|
+
}): Promise<A['result']> {
|
|
160
164
|
switch (action.type) {
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
case 'create':
|
|
166
|
+
return this.data.create({ item: action.payload, execution })
|
|
167
|
+
|
|
168
|
+
case 'createMany':
|
|
169
|
+
return this.data.createMany({ items: action.payload, execution })
|
|
170
|
+
|
|
171
|
+
case 'update':
|
|
172
|
+
return this.data.update({ item: action.payload, execution })
|
|
173
|
+
|
|
174
|
+
case 'updateMany':
|
|
175
|
+
return this.data.updateMany({ items: action.payload, execution })
|
|
176
|
+
|
|
177
|
+
case 'upsert':
|
|
178
|
+
return this.data.upsert({ item: action.payload, execution })
|
|
179
|
+
|
|
180
|
+
case 'upsertMany':
|
|
181
|
+
return this.data.upsertMany({ items: action.payload, execution })
|
|
182
|
+
|
|
183
|
+
case 'delete':
|
|
184
|
+
return this.data.delete({ id: action.payload, execution })
|
|
185
|
+
|
|
186
|
+
case 'deleteMany':
|
|
187
|
+
return this.data.deleteMany({ ids: action.payload, execution })
|
|
188
|
+
|
|
189
|
+
default:
|
|
163
190
|
throw new ExhaustiveSwitchCheck(action)
|
|
164
|
-
|
|
191
|
+
|
|
165
192
|
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function prepareActionDefinitions({ model, meta, }) {
|
|
170
|
-
return {
|
|
171
|
-
create: {
|
|
172
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_Create`,
|
|
173
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameCreate,
|
|
174
|
-
payload: meta.types.dto.create,
|
|
175
|
-
resultType: meta.types.typeName,
|
|
176
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.create)],
|
|
177
|
-
dispatcherParameterName: 'item',
|
|
178
|
-
},
|
|
179
|
-
createMany: {
|
|
180
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_CreateMany`,
|
|
181
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameCreateMany,
|
|
182
|
-
payload: `${meta.types.dto.create}[]`,
|
|
183
|
-
resultType: `${meta.types.typeName}[]`,
|
|
184
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.create)],
|
|
185
|
-
dispatcherParameterName: 'items',
|
|
186
|
-
},
|
|
187
|
-
update: {
|
|
188
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_Update`,
|
|
189
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameUpdate,
|
|
190
|
-
payload: meta.types.dto.update,
|
|
191
|
-
resultType: meta.types.typeName,
|
|
192
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.update)],
|
|
193
|
-
dispatcherParameterName: 'item',
|
|
194
|
-
},
|
|
195
|
-
updateMany: {
|
|
196
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_UpdateMany`,
|
|
197
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameUpdateMany,
|
|
198
|
-
payload: `${meta.types.dto.update}[]`,
|
|
199
|
-
resultType: `${meta.types.typeName}[]`,
|
|
200
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.update)],
|
|
201
|
-
dispatcherParameterName: 'items',
|
|
202
|
-
},
|
|
203
|
-
upsert: {
|
|
204
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_Upsert`,
|
|
205
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameUpsert,
|
|
206
|
-
payload: meta.types.dto.upsert,
|
|
207
|
-
resultType: meta.types.typeName,
|
|
208
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.upsert)],
|
|
209
|
-
dispatcherParameterName: 'item',
|
|
210
|
-
},
|
|
211
|
-
upsertMany: {
|
|
212
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_UpsertMany`,
|
|
213
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameUpsertMany,
|
|
214
|
-
payload: `${meta.types.dto.upsert}[]`,
|
|
215
|
-
resultType: `${meta.types.typeName}[]`,
|
|
216
|
-
imports: [(0, types_1.toAnnotatedTypeName)(meta.types.dto.upsert)],
|
|
217
|
-
dispatcherParameterName: 'items',
|
|
218
|
-
},
|
|
219
|
-
delete: {
|
|
220
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_Delete`,
|
|
221
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameDelete,
|
|
222
|
-
payload: model.brandedIdType,
|
|
223
|
-
resultType: model.brandedIdType,
|
|
224
|
-
imports: [(0, types_1.toAnnotatedTypeName)(model.brandedIdType)],
|
|
225
|
-
dispatcherParameterName: 'id',
|
|
226
|
-
},
|
|
227
|
-
deleteMany: {
|
|
228
|
-
actionName: `Action_${meta.businessLogic.update.actionNameModelPart}_DeleteMany`,
|
|
229
|
-
createFunctionName: meta.businessLogic.update.createActionFunctionNameDeleteMany,
|
|
230
|
-
payload: `${model.brandedIdType}[]`,
|
|
231
|
-
resultType: `${model.brandedIdType}[]`,
|
|
232
|
-
imports: [(0, types_1.toAnnotatedTypeName)(model.brandedIdType)],
|
|
233
|
-
dispatcherParameterName: 'ids',
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function generateAction({ meta, type, config: { imports, actionName, payload, createFunctionName, resultType, dispatcherParameterName }, }) {
|
|
238
|
-
return {
|
|
239
|
-
actionName,
|
|
240
|
-
actionType: type,
|
|
241
|
-
importTypes: imports,
|
|
242
|
-
resultMap: `${type}: ${resultType}`,
|
|
243
|
-
typeDefinition: `
|
|
244
|
-
export type ${actionName} = { scope: '${meta.actions.actionScopeConstType}'; type: '${type}'; payload: ${payload} }`,
|
|
245
|
-
createFunction: `
|
|
246
|
-
export function ${createFunctionName}(payload: ${payload}): ${actionName} {
|
|
247
|
-
return { scope: '${meta.actions.actionScopeConstType}', type: '${type}', payload }
|
|
248
|
-
}`,
|
|
249
|
-
dispatchCaseExpression: `
|
|
250
|
-
case '${type}':
|
|
251
|
-
return this.${type}({ ${dispatcherParameterName}: action.payload, execution })`,
|
|
252
|
-
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
253
196
|
}
|
|
197
|
+
exports.generateModelBusinessLogicUpdate = generateModelBusinessLogicUpdate;
|
|
@@ -5,7 +5,9 @@ const imports_1 = require("../../lib/imports");
|
|
|
5
5
|
const meta_1 = require("../../lib/meta");
|
|
6
6
|
const fields_1 = require("../../lib/schema/fields");
|
|
7
7
|
const types_1 = require("../../lib/schema/types");
|
|
8
|
+
const types_2 = require("../../lib/types");
|
|
8
9
|
const ast_1 = require("../../lib/utils/ast");
|
|
10
|
+
const jsdoc_1 = require("../../lib/utils/jsdoc");
|
|
9
11
|
/**
|
|
10
12
|
* Generates view business logic for a given model.
|
|
11
13
|
* The view logic exposes all information and links of a model. See template's readme for more info.
|
|
@@ -56,10 +58,18 @@ function generateModelBusinessLogicView({ model, meta }) {
|
|
|
56
58
|
variableName: relationVariableName,
|
|
57
59
|
variableDefinition: `const ${relationVariableName} = ${relationVariableDefinition}`,
|
|
58
60
|
});
|
|
61
|
+
if (relation.relationToModel.typeName !== model.typeName) {
|
|
62
|
+
imports.addImport({ from: refMeta.types.filePath, items: [refMeta.types.toBrandedIdTypeFnName] });
|
|
63
|
+
imports.addTypeImport({ from: refMeta.types.filePath, items: [refModel.brandedIdType, refMeta.types.typeName] });
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
const hasLinkedItems = variables.size > 0;
|
|
61
67
|
if (hasLinkedItems) {
|
|
62
|
-
|
|
68
|
+
// NOTE: If we need to generate the linked item type, we need to import the enum types.
|
|
69
|
+
for (const enumField of (0, fields_1.getEnumFields)(model)) {
|
|
70
|
+
const enumMeta = (0, meta_1.getEnumMetadata)(enumField);
|
|
71
|
+
imports.addTypeImport({ from: enumMeta.types.filePath, items: [enumField.typeName] });
|
|
72
|
+
}
|
|
63
73
|
}
|
|
64
74
|
const linkedItemsGetterFn = `
|
|
65
75
|
/**
|
|
@@ -91,6 +101,16 @@ function generateModelBusinessLogicView({ model, meta }) {
|
|
|
91
101
|
|
|
92
102
|
return item
|
|
93
103
|
}
|
|
104
|
+
`;
|
|
105
|
+
const linkedTypeDefinition = `
|
|
106
|
+
export type ${meta.types.linkedTypeName} = {
|
|
107
|
+
${model.fields
|
|
108
|
+
.map((f) => `
|
|
109
|
+
${(0, jsdoc_1.getFieldComment)(f)}
|
|
110
|
+
${getLinkedFieldType(f)}${f.isRequired ? '' : ' | null'}
|
|
111
|
+
`)
|
|
112
|
+
.join('\n')}
|
|
113
|
+
}
|
|
94
114
|
`;
|
|
95
115
|
return /* ts */ `
|
|
96
116
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
@@ -99,6 +119,8 @@ import { FilterOperator } from '@backend/common'
|
|
|
99
119
|
|
|
100
120
|
${imports.generate()}
|
|
101
121
|
|
|
122
|
+
${hasLinkedItems ? linkedTypeDefinition : ''}
|
|
123
|
+
|
|
102
124
|
@Injectable()
|
|
103
125
|
export class ${meta.businessLogic.view.serviceClassName} {
|
|
104
126
|
constructor(${constructorParameters.join(',\n')}) {}
|
|
@@ -252,3 +274,20 @@ function compare(a: ${model.typeName}, b: ${model.typeName}, field: keyof ${mode
|
|
|
252
274
|
|
|
253
275
|
`;
|
|
254
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Converts a field to a TypeScript type definition with linked fields.
|
|
279
|
+
*/
|
|
280
|
+
function getLinkedFieldType(f) {
|
|
281
|
+
switch (f.kind) {
|
|
282
|
+
case 'enum':
|
|
283
|
+
return `${f.name}: ${f.typeName}`;
|
|
284
|
+
case 'relation':
|
|
285
|
+
return `${f.relationFieldName}: ${f.relationToModel.typeName}`;
|
|
286
|
+
case 'id':
|
|
287
|
+
return `${f.name}: ${f.model.brandedIdType}`;
|
|
288
|
+
case 'scalar':
|
|
289
|
+
return `${f.name}: ${f.tsTypeName}`;
|
|
290
|
+
default:
|
|
291
|
+
throw new types_2.ExhaustiveSwitchCheck(f);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
@@ -9,28 +9,18 @@ const types_1 = require("../../lib/schema/types");
|
|
|
9
9
|
*/
|
|
10
10
|
function generateRoute({ model, meta }) {
|
|
11
11
|
const { idField, defaultField } = model;
|
|
12
|
+
const { scopeName, dataRepositoryVariableName } = meta.businessLogic;
|
|
12
13
|
const defaultValueMethod = `
|
|
13
|
-
getDefault: procedure.query(({ ctx }) => ctx.view.${meta.data.dataServiceName}.${
|
|
14
|
+
getDefault: procedure.query(({ ctx }) => ctx.view.${meta.data.dataServiceName}.${dataRepositoryVariableName}.defaultValue),
|
|
14
15
|
`;
|
|
16
|
+
const { zodCreateObject, zodUpdateObject, zodUpsertObject } = meta.businessLogic.update;
|
|
15
17
|
const imports = imports_1.ImportsGenerator.from(meta.trpc.routerFilePath).addImports({
|
|
16
18
|
[meta.types.importPath]: [
|
|
17
19
|
(0, types_1.toAnnotatedTypeName)(model.typeName),
|
|
18
20
|
meta.types.toBrandedIdTypeFnName,
|
|
19
|
-
meta.types.zodDecoderFnNames.createObject,
|
|
20
|
-
meta.types.zodDecoderFnNames.updateObject,
|
|
21
|
-
meta.types.zodDecoderFnNames.upsertObject,
|
|
22
21
|
meta.types.zodDecoderFnNames.id,
|
|
23
22
|
],
|
|
24
|
-
[meta.businessLogic.
|
|
25
|
-
meta.businessLogic.update.createActionFunctionNameCreate,
|
|
26
|
-
meta.businessLogic.update.createActionFunctionNameCreateMany,
|
|
27
|
-
meta.businessLogic.update.createActionFunctionNameUpdate,
|
|
28
|
-
meta.businessLogic.update.createActionFunctionNameUpdateMany,
|
|
29
|
-
meta.businessLogic.update.createActionFunctionNameUpsert,
|
|
30
|
-
meta.businessLogic.update.createActionFunctionNameUpsertMany,
|
|
31
|
-
meta.businessLogic.update.createActionFunctionNameDelete,
|
|
32
|
-
meta.businessLogic.update.createActionFunctionNameDeleteMany,
|
|
33
|
-
],
|
|
23
|
+
[meta.businessLogic.update.serviceFilePath]: [zodCreateObject, zodUpdateObject, zodUpsertObject],
|
|
34
24
|
});
|
|
35
25
|
return /* ts */ `
|
|
36
26
|
import { z } from 'zod'
|
|
@@ -71,30 +61,37 @@ export const ${meta.trpc.routerName} = router({
|
|
|
71
61
|
return ctx.view.${meta.data.dataServiceName}.getList(input)
|
|
72
62
|
}),
|
|
73
63
|
|
|
74
|
-
create: procedure
|
|
75
|
-
.
|
|
64
|
+
create: procedure
|
|
65
|
+
.input(${zodCreateObject})
|
|
66
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "create", payload: input})),
|
|
76
67
|
|
|
77
|
-
createMany: procedure
|
|
78
|
-
.
|
|
68
|
+
createMany: procedure
|
|
69
|
+
.input(z.array(${zodCreateObject}))
|
|
70
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "createMany", payload: input})),
|
|
79
71
|
|
|
80
|
-
update: procedure
|
|
81
|
-
.
|
|
72
|
+
update: procedure
|
|
73
|
+
.input(${zodUpdateObject})
|
|
74
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "update", payload: input})),
|
|
82
75
|
|
|
83
|
-
updateMany: procedure
|
|
84
|
-
.
|
|
76
|
+
updateMany: procedure
|
|
77
|
+
.input(z.array(${zodUpdateObject}))
|
|
78
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "updateMany", payload: input})),
|
|
85
79
|
|
|
86
|
-
upsert: procedure
|
|
87
|
-
.
|
|
80
|
+
upsert: procedure
|
|
81
|
+
.input(${zodUpsertObject})
|
|
82
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "upsert", payload: input})),
|
|
88
83
|
|
|
89
|
-
upsertMany: procedure
|
|
90
|
-
.
|
|
84
|
+
upsertMany: procedure
|
|
85
|
+
.input(z.array(${zodUpsertObject}))
|
|
86
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "upsertMany", payload: input})),
|
|
91
87
|
|
|
92
|
-
delete: procedure
|
|
93
|
-
.
|
|
88
|
+
delete: procedure
|
|
89
|
+
.input(${meta.types.zodDecoderFnNames.id})
|
|
90
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "delete", payload: input})),
|
|
94
91
|
|
|
95
|
-
deleteMany: procedure
|
|
96
|
-
.
|
|
97
|
-
|
|
92
|
+
deleteMany: procedure
|
|
93
|
+
.input(z.array(${meta.types.zodDecoderFnNames.id}))
|
|
94
|
+
.mutation(({ input, ctx }) => ctx.dispatch({scope: "${scopeName}", type: "deleteMany", payload: input})),
|
|
98
95
|
})
|
|
99
96
|
`;
|
|
100
97
|
}
|
|
@@ -14,27 +14,6 @@ function generateModelTypes({ model, meta }) {
|
|
|
14
14
|
var _a;
|
|
15
15
|
const idField = model.idField;
|
|
16
16
|
const imports = imports_1.ImportsGenerator.from(meta.types.filePath);
|
|
17
|
-
/**
|
|
18
|
-
* Tells whether model references other models.
|
|
19
|
-
*/
|
|
20
|
-
let hasLinkedItems = false;
|
|
21
|
-
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
22
|
-
hasLinkedItems = true;
|
|
23
|
-
if (relation.relationToModel.typeName === model.typeName) {
|
|
24
|
-
// NOTE: All type definitions are already present in this file for this model.
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const refModel = relation.relationToModel;
|
|
28
|
-
const refMeta = (0, meta_1.getModelMetadata)({ model: refModel });
|
|
29
|
-
imports.addImport({
|
|
30
|
-
items: [refMeta.types.toBrandedIdTypeFnName],
|
|
31
|
-
from: refMeta.types.filePath,
|
|
32
|
-
});
|
|
33
|
-
imports.addTypeImport({
|
|
34
|
-
items: [refModel.brandedIdType, refMeta.types.typeName],
|
|
35
|
-
from: refMeta.types.filePath,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
17
|
for (const f of (0, fields_1.getEnumFields)(model)) {
|
|
39
18
|
const refEnumMeta = (0, meta_1.getEnumMetadata)({ enumerator: f.enumerator });
|
|
40
19
|
imports.addTypeImport({
|
|
@@ -48,16 +27,15 @@ function generateModelTypes({ model, meta }) {
|
|
|
48
27
|
from: schemaMeta.types.dto.path,
|
|
49
28
|
});
|
|
50
29
|
const decoderNames = meta.types.zodDecoderFnNames;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
30
|
+
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
31
|
+
if (relation.relationToModel.typeName === model.typeName) {
|
|
32
|
+
// NOTE: All type definitions are already present in this file for this model.
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const refMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
|
|
36
|
+
imports.addImport({ from: refMeta.types.filePath, items: [refMeta.types.toBrandedIdTypeFnName] });
|
|
37
|
+
imports.addTypeImport({ from: refMeta.types.filePath, items: [relation.relationToModel.brandedIdType] });
|
|
59
38
|
}
|
|
60
|
-
`;
|
|
61
39
|
return /* ts */ `
|
|
62
40
|
import { z } from 'zod'
|
|
63
41
|
|
|
@@ -68,14 +46,14 @@ export type ${meta.types.typeName} = {
|
|
|
68
46
|
${model.fields
|
|
69
47
|
.map((f) => {
|
|
70
48
|
return `
|
|
71
|
-
${getFieldComment(f)}
|
|
49
|
+
${(0, jsdoc_1.getFieldComment)(f)}
|
|
72
50
|
${f.name}: ${getFieldType(f)}${f.isRequired ? '' : ' | null'}
|
|
73
51
|
`;
|
|
74
52
|
})
|
|
75
53
|
.join('\n')}
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
|
|
56
|
+
|
|
79
57
|
|
|
80
58
|
/**
|
|
81
59
|
* Branded Id type that should be used to identify an instance of a ${meta.userFriendlyName}.
|
|
@@ -103,31 +81,6 @@ export const ${decoderNames.fromDatabase} = z.object({
|
|
|
103
81
|
${model.fields.map((field) => `${field.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field })}`).join(',')}
|
|
104
82
|
})
|
|
105
83
|
|
|
106
|
-
/**
|
|
107
|
-
* Zod decoder for validating the create input of a ${meta.userFriendlyName}.
|
|
108
|
-
*/
|
|
109
|
-
export const ${decoderNames.createObject} = z.object({
|
|
110
|
-
${model.fields
|
|
111
|
-
.filter((f) => !f.attributes.isReadonly)
|
|
112
|
-
.map((field) => `${field.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field })}`)
|
|
113
|
-
.join(',')}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Zod decoder for validating the update input of a ${meta.userFriendlyName} .
|
|
118
|
-
*/
|
|
119
|
-
export const ${decoderNames.updateObject} = z.object({
|
|
120
|
-
${model.fields
|
|
121
|
-
.filter((f) => !f.attributes.isReadonly || f.kind === 'id')
|
|
122
|
-
.map((field) => `${field.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field, allowAnyOptionalField: field.kind !== 'id' })}`)
|
|
123
|
-
.join(',')}
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Zod decoder for validating the upsert input of a ${meta.userFriendlyName} .
|
|
128
|
-
*/
|
|
129
|
-
export const ${decoderNames.upsertObject} = z.union([${decoderNames.updateObject}, ${decoderNames.createObject}])
|
|
130
|
-
|
|
131
84
|
/**
|
|
132
85
|
* Data transfer object for creating a new ${meta.userFriendlyName} instance.
|
|
133
86
|
*/
|
|
@@ -145,31 +98,6 @@ export type ${meta.types.dto.upsert} = ${schemaMeta.types.dto.upsert}<${meta.typ
|
|
|
145
98
|
`;
|
|
146
99
|
}
|
|
147
100
|
exports.generateModelTypes = generateModelTypes;
|
|
148
|
-
function getFieldComment(f) {
|
|
149
|
-
const examples = getFieldExamples(f);
|
|
150
|
-
let comment = '';
|
|
151
|
-
if (f.description) {
|
|
152
|
-
comment = ` * ${f.description.split('\n').join('\n * ')}\n`;
|
|
153
|
-
}
|
|
154
|
-
else if (f.kind === 'enum' && f.enumerator.description) {
|
|
155
|
-
comment = ` * ${f.enumerator.description.split('\n').join('\n * ')}\n`;
|
|
156
|
-
}
|
|
157
|
-
if (examples) {
|
|
158
|
-
comment += ` * ${examples}\n`;
|
|
159
|
-
}
|
|
160
|
-
if (comment === '') {
|
|
161
|
-
return '';
|
|
162
|
-
}
|
|
163
|
-
return `
|
|
164
|
-
/**
|
|
165
|
-
${comment}*/`;
|
|
166
|
-
}
|
|
167
|
-
function getFieldExamples(f) {
|
|
168
|
-
if (!f.attributes.examples) {
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
return `Examples: ${f.attributes.examples.map((e) => `"${e}"`).join(', ')}`;
|
|
172
|
-
}
|
|
173
101
|
/**
|
|
174
102
|
* Converts a field to a TypeScript type definition.
|
|
175
103
|
*/
|
|
@@ -187,20 +115,3 @@ function getFieldType(f) {
|
|
|
187
115
|
throw new types_1.ExhaustiveSwitchCheck(f);
|
|
188
116
|
}
|
|
189
117
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Converts a field to a TypeScript type definition with linked fields.
|
|
192
|
-
*/
|
|
193
|
-
function getLinkedFieldType(f) {
|
|
194
|
-
switch (f.kind) {
|
|
195
|
-
case 'enum':
|
|
196
|
-
return `${f.name}: ${f.typeName}`;
|
|
197
|
-
case 'relation':
|
|
198
|
-
return `${f.relationFieldName}: ${f.relationToModel.typeName}`;
|
|
199
|
-
case 'id':
|
|
200
|
-
return `${f.name}: ${f.model.brandedIdType}`;
|
|
201
|
-
case 'scalar':
|
|
202
|
-
return `${f.name}: ${f.tsTypeName}`;
|
|
203
|
-
default:
|
|
204
|
-
throw new types_1.ExhaustiveSwitchCheck(f);
|
|
205
|
-
}
|
|
206
|
-
}
|
package/dist/lib/exports.d.ts
CHANGED
|
@@ -34,6 +34,10 @@ export declare class ExportsGenerator {
|
|
|
34
34
|
* NOTE: This should only be used when the generator is a root generator.
|
|
35
35
|
*/
|
|
36
36
|
exportEverythingFromFile(file: string): ExportsGenerator;
|
|
37
|
+
/**
|
|
38
|
+
* Adds a given file to the collection of files we're exporting a selection from.
|
|
39
|
+
*/
|
|
40
|
+
exportSelectionFromPath(from: Types.FilePath, elements: Types.ImportableTypes[]): ExportsGenerator;
|
|
37
41
|
/**
|
|
38
42
|
* Returns the TypeScript export statements.
|
|
39
43
|
*/
|
package/dist/lib/exports.js
CHANGED
|
@@ -8,7 +8,7 @@ const file_1 = require("./utils/file");
|
|
|
8
8
|
class ExportsGenerator {
|
|
9
9
|
constructor({ path, isRoot }) {
|
|
10
10
|
this._path = path;
|
|
11
|
-
this._exports = new
|
|
11
|
+
this._exports = new Map();
|
|
12
12
|
this.isRoot = isRoot !== null && isRoot !== void 0 ? isRoot : false;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
@@ -31,7 +31,7 @@ class ExportsGenerator {
|
|
|
31
31
|
throw new Error(`Cannot use "exportEverythingFromPath" on a root generator.`);
|
|
32
32
|
}
|
|
33
33
|
const resolvedPath = (0, file_1.getRelativePath)({ from: this._path, to: from });
|
|
34
|
-
this._exports.
|
|
34
|
+
this._exports.set(resolvedPath, { kind: 'wildcard' });
|
|
35
35
|
return this;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
@@ -43,18 +43,48 @@ class ExportsGenerator {
|
|
|
43
43
|
if (!this.isRoot) {
|
|
44
44
|
throw new Error(`Cannot use "exportEverythingFromFile" on a non-root generator.`);
|
|
45
45
|
}
|
|
46
|
-
this._exports.
|
|
46
|
+
this._exports.set(file, { kind: 'wildcard' });
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Adds a given file to the collection of files we're exporting a selection from.
|
|
51
|
+
*/
|
|
52
|
+
exportSelectionFromPath(from, elements) {
|
|
53
|
+
if (this.isRoot) {
|
|
54
|
+
throw new Error(`Cannot use "exportSelectionFromPath" on a root generator.`);
|
|
55
|
+
}
|
|
56
|
+
const resolvedPath = (0, file_1.getRelativePath)({ from: this._path, to: from });
|
|
57
|
+
const _existing = this._exports.get(resolvedPath);
|
|
58
|
+
// NOTE: If we already export everything, we don't need to do anything.
|
|
59
|
+
if ((_existing === null || _existing === void 0 ? void 0 : _existing.kind) === 'wildcard') {
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
if ((_existing === null || _existing === void 0 ? void 0 : _existing.kind) === 'selection') {
|
|
63
|
+
for (const element of elements) {
|
|
64
|
+
_existing.elements.add(element);
|
|
65
|
+
}
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
this._exports.set(resolvedPath, { kind: 'selection', elements: new Set(elements) });
|
|
47
69
|
return this;
|
|
48
70
|
}
|
|
49
71
|
/**
|
|
50
72
|
* Returns the TypeScript export statements.
|
|
51
73
|
*/
|
|
52
74
|
generate() {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.
|
|
57
|
-
|
|
75
|
+
const statements = [];
|
|
76
|
+
const exports = Array.from(this._exports.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
77
|
+
for (const [_path, _export] of exports) {
|
|
78
|
+
switch (_export.kind) {
|
|
79
|
+
case 'wildcard':
|
|
80
|
+
statements.push(`export * from '${_path}'`);
|
|
81
|
+
break;
|
|
82
|
+
case 'selection':
|
|
83
|
+
statements.push(`export { ${Array.from(_export.elements).join(', ')} } from '${_path}'`);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return statements.join('\n');
|
|
58
88
|
}
|
|
59
89
|
}
|
|
60
90
|
exports.ExportsGenerator = ExportsGenerator;
|
package/dist/lib/meta.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export type SchemaMetaData = {
|
|
|
107
107
|
*/
|
|
108
108
|
mock: Types.ClassName;
|
|
109
109
|
};
|
|
110
|
-
|
|
110
|
+
dispatcher: {
|
|
111
111
|
/**
|
|
112
112
|
* Path to the file containing the dispatcher service class definition.
|
|
113
113
|
*/
|
|
@@ -116,6 +116,14 @@ export type SchemaMetaData = {
|
|
|
116
116
|
* The name of the dispatcher service class.
|
|
117
117
|
*/
|
|
118
118
|
class: Types.ClassName;
|
|
119
|
+
/**
|
|
120
|
+
* The name of the interface that indicates a service can dispatch actions.
|
|
121
|
+
*/
|
|
122
|
+
interface: Types.ClassName;
|
|
123
|
+
/**
|
|
124
|
+
* The name of a util type that is used to create a dictionary of action payloads and results from the action descriptions.
|
|
125
|
+
*/
|
|
126
|
+
actionMethod: Types.TypeName;
|
|
119
127
|
};
|
|
120
128
|
};
|
|
121
129
|
/**
|
|
@@ -917,50 +925,17 @@ export type ModelMetaData = {
|
|
|
917
925
|
*/
|
|
918
926
|
actionModelDiscriminantName: Types.VariableName;
|
|
919
927
|
/**
|
|
920
|
-
*
|
|
921
|
-
*/
|
|
922
|
-
actionName: Types.VariableName;
|
|
923
|
-
/**
|
|
924
|
-
* Name of the model's aggregated action result type. (e.g. `ActionResult_Aggregation`)
|
|
925
|
-
*/
|
|
926
|
-
actionResultName: Types.VariableName;
|
|
927
|
-
/**
|
|
928
|
-
* The model's name that is used to build the different action types.
|
|
929
|
-
* (e.g. `Aggregation` which will be used to build `Action_Aggregation_Create`)
|
|
930
|
-
*/
|
|
931
|
-
actionNameModelPart: Types.VariableName;
|
|
932
|
-
/**
|
|
933
|
-
* Name of the function that creates a `create` action for the model (e.g. `createActionAggregationCreate`)
|
|
934
|
-
*/
|
|
935
|
-
createActionFunctionNameCreate: Types.FunctionName;
|
|
936
|
-
/**
|
|
937
|
-
* Name of the function that creates a `createMany` action for the model (e.g. `createActionAggregationCreateMany`)
|
|
938
|
-
*/
|
|
939
|
-
createActionFunctionNameCreateMany: Types.FunctionName;
|
|
940
|
-
/**
|
|
941
|
-
* Name of the function that creates a `update` action for the model (e.g. `createActionAggregationUpdate`)
|
|
942
|
-
*/
|
|
943
|
-
createActionFunctionNameUpdate: Types.FunctionName;
|
|
944
|
-
/**
|
|
945
|
-
* Name of the function that creates a `updateMany` action for the model (e.g. `createActionAggregationUpdateMany`)
|
|
946
|
-
*/
|
|
947
|
-
createActionFunctionNameUpdateMany: Types.FunctionName;
|
|
948
|
-
/**
|
|
949
|
-
* Name of the function that creates a `upsert` action for the model (e.g. `createActionAggregationUpsert`)
|
|
950
|
-
*/
|
|
951
|
-
createActionFunctionNameUpsert: Types.FunctionName;
|
|
952
|
-
/**
|
|
953
|
-
* Name of the function that creates a `upsertMany` action for the model (e.g. `createActionAggregationUpsertMany`)
|
|
928
|
+
* The name of the function that decodes a Create object to a fully typed object, e.g. `aggregationCreateDecoder`.
|
|
954
929
|
*/
|
|
955
|
-
|
|
930
|
+
zodCreateObject: Types.FunctionName;
|
|
956
931
|
/**
|
|
957
|
-
*
|
|
932
|
+
* The name of the function that decodes an Update object to a fully typed object, e.g. `aggregationUpdateDecoder`.
|
|
958
933
|
*/
|
|
959
|
-
|
|
934
|
+
zodUpdateObject: Types.FunctionName;
|
|
960
935
|
/**
|
|
961
|
-
*
|
|
936
|
+
* The name of the function that decodes an Upsert object to a fully typed object, e.g. `aggregationUpsertDecoder`.
|
|
962
937
|
*/
|
|
963
|
-
|
|
938
|
+
zodUpsertObject: Types.FunctionName;
|
|
964
939
|
};
|
|
965
940
|
/**
|
|
966
941
|
* Name by which the business logic service exposes the data service.
|
|
@@ -1120,18 +1095,6 @@ export type ModelMetaData = {
|
|
|
1120
1095
|
* The name of the function that decodes a source (database) object to a fully typed object, e.g. `aggregationDatabaseDecoder`.
|
|
1121
1096
|
*/
|
|
1122
1097
|
fromDatabase: Types.FunctionName;
|
|
1123
|
-
/**
|
|
1124
|
-
* The name of the function that decodes a Create object to a fully typed object, e.g. `aggregationCreateDecoder`.
|
|
1125
|
-
*/
|
|
1126
|
-
createObject: Types.FunctionName;
|
|
1127
|
-
/**
|
|
1128
|
-
* The name of the function that decodes an Update object to a fully typed object, e.g. `aggregationUpdateDecoder`.
|
|
1129
|
-
*/
|
|
1130
|
-
updateObject: Types.FunctionName;
|
|
1131
|
-
/**
|
|
1132
|
-
* The name of the function that decodes an Upsert object to a fully typed object, e.g. `aggregationUpsertDecoder`.
|
|
1133
|
-
*/
|
|
1134
|
-
upsertObject: Types.FunctionName;
|
|
1135
1098
|
};
|
|
1136
1099
|
/**
|
|
1137
1100
|
* Type definitions for the different Data Transfer Objects (DTOs).
|
package/dist/lib/meta.js
CHANGED
|
@@ -86,9 +86,11 @@ function getSchemaMetadata({ config }) {
|
|
|
86
86
|
class: Types.toClassName(`ActionExecution`),
|
|
87
87
|
mock: Types.toClassName(`MockActionExecution`),
|
|
88
88
|
},
|
|
89
|
-
|
|
89
|
+
dispatcher: {
|
|
90
90
|
filePath: Types.toPath(`${config.paths.actionsPath}dispatcher.service`),
|
|
91
91
|
class: Types.toClassName(`DispatcherService`),
|
|
92
|
+
interface: Types.toClassName(`IDispatcher`),
|
|
93
|
+
actionMethod: Types.toTypeName(`ActionMethod`),
|
|
92
94
|
},
|
|
93
95
|
},
|
|
94
96
|
businessLogic: {
|
|
@@ -335,17 +337,9 @@ function getModelMetadata({ model }) {
|
|
|
335
337
|
serviceFileName: Types.toFileName(`${camelCase}.update.service`),
|
|
336
338
|
serviceFilePath: Types.toPath(`${config.paths.businessLogicPath}update/${camelCase}.update.service`),
|
|
337
339
|
actionModelDiscriminantName: Types.toVariableName(`${camelCase}`),
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
createActionFunctionNameCreate: Types.toFunctionName(`createAction${PascalCase}Create`),
|
|
342
|
-
createActionFunctionNameCreateMany: Types.toFunctionName(`createAction${PascalCase}CreateMany`),
|
|
343
|
-
createActionFunctionNameUpdate: Types.toFunctionName(`createAction${PascalCase}Update`),
|
|
344
|
-
createActionFunctionNameUpdateMany: Types.toFunctionName(`createAction${PascalCase}UpdateMany`),
|
|
345
|
-
createActionFunctionNameUpsert: Types.toFunctionName(`createAction${PascalCase}Upsert`),
|
|
346
|
-
createActionFunctionNameUpsertMany: Types.toFunctionName(`createAction${PascalCase}UpsertMany`),
|
|
347
|
-
createActionFunctionNameDelete: Types.toFunctionName(`createAction${PascalCase}Delete`),
|
|
348
|
-
createActionFunctionNameDeleteMany: Types.toFunctionName(`createAction${PascalCase}DeleteMany`),
|
|
340
|
+
zodCreateObject: Types.toFunctionName(`${camelCase}CreateDecoder`),
|
|
341
|
+
zodUpdateObject: Types.toFunctionName(`${camelCase}UpdateDecoder`),
|
|
342
|
+
zodUpsertObject: Types.toFunctionName(`${camelCase}UpsertDecoder`),
|
|
349
343
|
},
|
|
350
344
|
dataRepositoryVariableName: Types.toVariableName(`data`),
|
|
351
345
|
},
|
|
@@ -409,9 +403,6 @@ function getModelMetadata({ model }) {
|
|
|
409
403
|
zodDecoderFnNames: {
|
|
410
404
|
id: Types.toFunctionName(`${camelCase}IdDecoder`),
|
|
411
405
|
fromDatabase: Types.toFunctionName(`${camelCase}DatabaseDecoder`),
|
|
412
|
-
createObject: Types.toFunctionName(`${camelCase}CreateDecoder`),
|
|
413
|
-
updateObject: Types.toFunctionName(`${camelCase}UpdateDecoder`),
|
|
414
|
-
upsertObject: Types.toFunctionName(`${camelCase}UpsertDecoder`),
|
|
415
406
|
},
|
|
416
407
|
dto: {
|
|
417
408
|
create: Types.toTypeName(`${PascalCase}CreateDTO`),
|
|
@@ -31,7 +31,7 @@ export type DiscriminantName = string & {
|
|
|
31
31
|
export declare const toDiscriminantName: (t: string) => DiscriminantName;
|
|
32
32
|
/**
|
|
33
33
|
* The name of a model, e.g. "Aggregation".
|
|
34
|
-
*/
|
|
34
|
+
*/
|
|
35
35
|
export type ModelName = string & {
|
|
36
36
|
readonly ___type: 'ModelName';
|
|
37
37
|
};
|
|
@@ -67,7 +67,7 @@ export declare const toAnnotatedTypeName: (name: TypeName) => AnnotatedTypeName;
|
|
|
67
67
|
export declare const isAnnotatedTypeName: (t: string | AnnotatedTypeName) => t is AnnotatedTypeName;
|
|
68
68
|
/**
|
|
69
69
|
* The name of a model field, e.g. "name".
|
|
70
|
-
*/
|
|
70
|
+
*/
|
|
71
71
|
export type FieldName = string & {
|
|
72
72
|
readonly ___type: 'FieldName';
|
|
73
73
|
};
|
|
@@ -77,7 +77,7 @@ export type FieldName = string & {
|
|
|
77
77
|
export declare const toFieldName: (t: string) => FieldName;
|
|
78
78
|
/**
|
|
79
79
|
* The name of an Enum, e.g. "Language".
|
|
80
|
-
*/
|
|
80
|
+
*/
|
|
81
81
|
export type EnumName = string & {
|
|
82
82
|
readonly ___type: 'EnumName';
|
|
83
83
|
};
|
|
@@ -87,7 +87,7 @@ export type EnumName = string & {
|
|
|
87
87
|
export declare const toEnumName: (t: string) => EnumName;
|
|
88
88
|
/**
|
|
89
89
|
* The name of a function (e.g. "toAggregation").
|
|
90
|
-
*/
|
|
90
|
+
*/
|
|
91
91
|
export type FunctionName = string & {
|
|
92
92
|
readonly ___type: 'FunctionName';
|
|
93
93
|
};
|
|
@@ -150,7 +150,7 @@ export declare const toPath: (t: string) => FilePath;
|
|
|
150
150
|
/**
|
|
151
151
|
* Branded string values that can be used as import statement values in the generators
|
|
152
152
|
*/
|
|
153
|
-
export type ImportableTypes = FunctionName | ClassName | AnnotatedTypeName | VariableName | EnumName;
|
|
153
|
+
export type ImportableTypes = FunctionName | ClassName | TypeName | AnnotatedTypeName | VariableName | EnumName;
|
|
154
154
|
/**
|
|
155
155
|
* Branded string values that can be used as paths in import statements
|
|
156
156
|
*/
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import type { Field } from '../schema/schema';
|
|
1
2
|
/**
|
|
2
3
|
* Returns a string of JSDoc comments from an array of lines.
|
|
3
4
|
*/
|
|
4
5
|
export declare function toJsDocComment(comments: string[] | undefined): string;
|
|
6
|
+
/**
|
|
7
|
+
* Returns a JSDoc comment for a field.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getFieldComment(f: Field): string;
|
package/dist/lib/utils/jsdoc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toJsDocComment = void 0;
|
|
3
|
+
exports.getFieldComment = exports.toJsDocComment = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Returns a string of JSDoc comments from an array of lines.
|
|
6
6
|
*/
|
|
@@ -14,3 +14,24 @@ function toJsDocComment(comments) {
|
|
|
14
14
|
.join('')} \n */`;
|
|
15
15
|
}
|
|
16
16
|
exports.toJsDocComment = toJsDocComment;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a JSDoc comment for a field.
|
|
19
|
+
*/
|
|
20
|
+
function getFieldComment(f) {
|
|
21
|
+
const lines = [];
|
|
22
|
+
if (f.description) {
|
|
23
|
+
lines.push(...f.description.split('\n'));
|
|
24
|
+
}
|
|
25
|
+
else if (f.kind === 'enum' && f.enumerator.description) {
|
|
26
|
+
lines.push(...f.enumerator.description.split('\n'));
|
|
27
|
+
}
|
|
28
|
+
const _examples = f.attributes.examples;
|
|
29
|
+
if (_examples) {
|
|
30
|
+
lines.push(`Examples: ${_examples.map((e) => `"${e}"`).join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
if (lines.length === 0) {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
return toJsDocComment(lines);
|
|
36
|
+
}
|
|
37
|
+
exports.getFieldComment = getFieldComment;
|