prisma-nestjs-graphql 21.2.0 → 22.0.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/README.md +13 -5
- package/bin.mjs +20 -0
- package/package.json +73 -96
- package/prisma-nestjs-graphql.d.ts +131 -0
- package/prisma-nestjs-graphql.mjs +2352 -0
- package/bin.js +0 -2
- package/generate.cjs +0 -2152
- package/generate.d.ts +0 -530
- package/index.cjs +0 -28
- package/index.d.ts +0 -2
|
@@ -0,0 +1,2352 @@
|
|
|
1
|
+
import { ok } from 'node:assert';
|
|
2
|
+
import awaitEventEmitterModule from 'await-event-emitter';
|
|
3
|
+
import { StructureKind, Project, QuoteKind } from 'ts-morph';
|
|
4
|
+
import lodash from 'lodash';
|
|
5
|
+
import { ok as ok$1 } from 'assert';
|
|
6
|
+
import JSON5 from 'json5';
|
|
7
|
+
import pupa from 'pupa';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import getRelativePath from 'get-relative-path';
|
|
10
|
+
import outmatch from 'outmatch';
|
|
11
|
+
import fs from 'graceful-fs';
|
|
12
|
+
import filenamify from 'filenamify';
|
|
13
|
+
import { unflatten } from 'flat';
|
|
14
|
+
import pluralize from 'pluralize';
|
|
15
|
+
|
|
16
|
+
function isManyAndReturnOutputType(name) {
|
|
17
|
+
const lowerName = name.toLowerCase();
|
|
18
|
+
if ((lowerName.startsWith('createmany') || lowerName.startsWith('updatemany')) && (lowerName.endsWith('andreturnoutputtype') || lowerName.endsWith('andreturn'))) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
camelCase,
|
|
26
|
+
castArray,
|
|
27
|
+
cloneDeep,
|
|
28
|
+
countBy,
|
|
29
|
+
first,
|
|
30
|
+
isEqual,
|
|
31
|
+
isObject,
|
|
32
|
+
kebabCase,
|
|
33
|
+
keyBy,
|
|
34
|
+
last,
|
|
35
|
+
mapKeys,
|
|
36
|
+
memoize,
|
|
37
|
+
merge,
|
|
38
|
+
omit,
|
|
39
|
+
partition,
|
|
40
|
+
remove,
|
|
41
|
+
startCase,
|
|
42
|
+
trim,
|
|
43
|
+
uniq,
|
|
44
|
+
uniqWith
|
|
45
|
+
} = lodash;
|
|
46
|
+
function pascalCase(string) {
|
|
47
|
+
return startCase(camelCase(string)).replaceAll(' ', '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* See https://github.com/prisma/prisma/blob/master/src/packages/client/src/generation/TSClient/Model.ts@getAggregationTypes
|
|
52
|
+
* Subcribes on: 'ArgsType'
|
|
53
|
+
*/
|
|
54
|
+
function argsType(field, args) {
|
|
55
|
+
if (['queryRaw', 'executeRaw'].includes(field.name)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (isManyAndReturnOutputType(field.name)) return;
|
|
59
|
+
const {
|
|
60
|
+
eventEmitter,
|
|
61
|
+
getModelName,
|
|
62
|
+
typeNames
|
|
63
|
+
} = args;
|
|
64
|
+
let className = pascalCase(`${field.name}Args`);
|
|
65
|
+
const modelName = getModelName(className) || '';
|
|
66
|
+
switch (className) {
|
|
67
|
+
case `Aggregate${modelName}Args`:
|
|
68
|
+
{
|
|
69
|
+
className = `${modelName}AggregateArgs`;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case `GroupBy${modelName}Args`:
|
|
73
|
+
{
|
|
74
|
+
className = `${modelName}GroupByArgs`;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const inputType = {
|
|
79
|
+
constraints: {
|
|
80
|
+
maxNumFields: null,
|
|
81
|
+
minNumFields: null
|
|
82
|
+
},
|
|
83
|
+
fields: [...field.args],
|
|
84
|
+
name: className
|
|
85
|
+
};
|
|
86
|
+
if (!field.args.some(x => x.name === '_count') && [`${modelName}AggregateArgs`, `${modelName}GroupByArgs`].includes(className)) {
|
|
87
|
+
const names = ['Count', 'Avg', 'Sum', 'Min', 'Max'];
|
|
88
|
+
if (`${modelName}GroupByArgs` === inputType.name) {
|
|
89
|
+
// Make `by` property array only, noEnumerable
|
|
90
|
+
const byField = inputType.fields.find(f => f.name === 'by');
|
|
91
|
+
if (byField?.inputTypes) {
|
|
92
|
+
byField.inputTypes = byField.inputTypes.filter(inputType => inputType.isList);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
if (!typeNames.has(`${modelName}${name}AggregateInput`)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
inputType.fields.push({
|
|
100
|
+
inputTypes: [{
|
|
101
|
+
isList: false,
|
|
102
|
+
location: 'inputObjectTypes',
|
|
103
|
+
type: `${modelName}${name}AggregateInput`
|
|
104
|
+
}],
|
|
105
|
+
isNullable: true,
|
|
106
|
+
isParameterizable: false,
|
|
107
|
+
// ?
|
|
108
|
+
isRequired: false,
|
|
109
|
+
name: `_${name.toLowerCase()}`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
eventEmitter.emitSync('InputType', {
|
|
114
|
+
...args,
|
|
115
|
+
classDecoratorName: 'ArgsType',
|
|
116
|
+
fileType: 'args',
|
|
117
|
+
inputType
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const BeforeGenerateField = 'BeforeGenerateField';
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Subscribes on 'BeforeInputType'
|
|
125
|
+
*/
|
|
126
|
+
function combineScalarFilters(eventEmitter) {
|
|
127
|
+
eventEmitter.on('BeforeInputType', beforeInputType$2);
|
|
128
|
+
eventEmitter.on(BeforeGenerateField, beforeGenerateField);
|
|
129
|
+
eventEmitter.on('PostBegin', postBegin);
|
|
130
|
+
}
|
|
131
|
+
function beforeInputType$2(args) {
|
|
132
|
+
const {
|
|
133
|
+
inputType,
|
|
134
|
+
removeTypes
|
|
135
|
+
} = args;
|
|
136
|
+
if (isContainBogus(inputType.name) && isScalarFilter(inputType)) {
|
|
137
|
+
removeTypes.add(inputType.name);
|
|
138
|
+
inputType.name = replaceBogus(inputType.name);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function beforeGenerateField(field) {
|
|
142
|
+
for (const fieldInput of field.inputTypes) {
|
|
143
|
+
if (fieldInput.location !== 'inputObjectTypes') {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const fieldInputType = String(fieldInput.type);
|
|
147
|
+
if (isContainBogus(fieldInputType)) {
|
|
148
|
+
fieldInput.type = replaceBogus(fieldInputType);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function replaceBogus(name) {
|
|
153
|
+
return name.replaceAll(/Nullable|Nested/g, '');
|
|
154
|
+
}
|
|
155
|
+
function isContainBogus(name) {
|
|
156
|
+
return name.startsWith('Nested') || name.includes('Nullable') && name.endsWith('Filter') || name.endsWith('NullableFilter');
|
|
157
|
+
}
|
|
158
|
+
function isScalarFilter(inputType) {
|
|
159
|
+
if (!inputType.name.endsWith('Filter')) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
let result = false;
|
|
163
|
+
const equals = inputType.fields.find(f => f.name === 'equals');
|
|
164
|
+
if (equals) {
|
|
165
|
+
result = equals.inputTypes.every(x => {
|
|
166
|
+
return ['enumTypes', 'scalar'].includes(x.location);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
function postBegin(args) {
|
|
172
|
+
const {
|
|
173
|
+
modelNames,
|
|
174
|
+
schema
|
|
175
|
+
} = args;
|
|
176
|
+
const inputTypes = schema.inputObjectTypes.prisma ?? [];
|
|
177
|
+
const enumTypes = schema.enumTypes.model || [];
|
|
178
|
+
const types = ['Bool', 'Int', 'String', 'DateTime', 'Decimal', 'Float', 'Json', 'Bytes', 'BigInt'];
|
|
179
|
+
for (const enumType of enumTypes) {
|
|
180
|
+
const {
|
|
181
|
+
name
|
|
182
|
+
} = enumType;
|
|
183
|
+
types.push(`Enum${name}`);
|
|
184
|
+
}
|
|
185
|
+
const inputTypeByName = keyBy(inputTypes, inputType => inputType.name);
|
|
186
|
+
const replaceBogusFilters = (filterName, filterNameCandidates) => {
|
|
187
|
+
for (const filterNameCandidate of filterNameCandidates) {
|
|
188
|
+
const candidate = inputTypeByName[filterNameCandidate];
|
|
189
|
+
if (candidate) {
|
|
190
|
+
const inputType = cloneDeep({
|
|
191
|
+
...candidate,
|
|
192
|
+
name: filterName
|
|
193
|
+
});
|
|
194
|
+
inputTypes.push(inputType);
|
|
195
|
+
inputTypeByName[filterName] = inputType;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
for (const type of types) {
|
|
201
|
+
// Scalar filters
|
|
202
|
+
replaceBogusFilters(`${type}Filter`, [`${type}NullableFilter`, `Nested${type}NullableFilter`]);
|
|
203
|
+
replaceBogusFilters(`${type}WithAggregatesFilter`, [`${type}NullableWithAggregatesFilter`, `Nested${type}NullableWithAggregatesFilter`]);
|
|
204
|
+
replaceBogusFilters(`${type}ListFilter`, [`${type}NullableListFilter`, `Nested${type}NullableListFilter`]);
|
|
205
|
+
}
|
|
206
|
+
for (const modelName of modelNames) {
|
|
207
|
+
replaceBogusFilters(`${modelName}RelationFilter`, [`${modelName}NullableRelationFilter`]);
|
|
208
|
+
}
|
|
209
|
+
for (const modelName of modelNames) {
|
|
210
|
+
replaceBogusFilters(`${modelName}ScalarRelationFilter`, [`${modelName}NullableScalarRelationFilter`]);
|
|
211
|
+
}
|
|
212
|
+
remove(inputTypes, inputType => {
|
|
213
|
+
return isContainBogus(inputType.name);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Create aggregate inputs from aggregate outputs.
|
|
219
|
+
* See client/src/generation/TSClient.ts @ getAggregationTypes
|
|
220
|
+
* Subcribes on: 'AggregateOutput'
|
|
221
|
+
*/
|
|
222
|
+
function createAggregateInput(args) {
|
|
223
|
+
const {
|
|
224
|
+
eventEmitter,
|
|
225
|
+
outputType
|
|
226
|
+
} = args;
|
|
227
|
+
const className = `${outputType.name}Input`;
|
|
228
|
+
|
|
229
|
+
// console.dir({ outputType, className, __filename }, { depth: 5 });
|
|
230
|
+
|
|
231
|
+
const inputType = {
|
|
232
|
+
constraints: {
|
|
233
|
+
maxNumFields: null,
|
|
234
|
+
minNumFields: null
|
|
235
|
+
},
|
|
236
|
+
fields: outputType.fields.map(x => ({
|
|
237
|
+
inputTypes: [{
|
|
238
|
+
isList: false,
|
|
239
|
+
location: 'scalar',
|
|
240
|
+
type: 'true'
|
|
241
|
+
}],
|
|
242
|
+
isNullable: x.isNullable ?? true,
|
|
243
|
+
isParameterizable: false,
|
|
244
|
+
// ?
|
|
245
|
+
isRequired: false,
|
|
246
|
+
name: x.name
|
|
247
|
+
})),
|
|
248
|
+
name: className
|
|
249
|
+
};
|
|
250
|
+
eventEmitter.emitSync('InputType', {
|
|
251
|
+
...args,
|
|
252
|
+
classDecoratorName: 'InputType',
|
|
253
|
+
fileType: 'input',
|
|
254
|
+
inputType
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class ImportDeclarationMap extends Map {
|
|
259
|
+
add(name, value) {
|
|
260
|
+
if (!this.has(name)) {
|
|
261
|
+
if (typeof value === 'string') {
|
|
262
|
+
this.set(name, {
|
|
263
|
+
moduleSpecifier: value,
|
|
264
|
+
namedImports: [{
|
|
265
|
+
name
|
|
266
|
+
}]
|
|
267
|
+
});
|
|
268
|
+
} else {
|
|
269
|
+
this.set(name, value);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
create(args) {
|
|
274
|
+
const {
|
|
275
|
+
config,
|
|
276
|
+
propertySettings,
|
|
277
|
+
propertyType
|
|
278
|
+
} = args;
|
|
279
|
+
if (propertySettings) {
|
|
280
|
+
return this.createFrom({
|
|
281
|
+
...propertySettings
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
if (/\bIdentity</.test(propertyType)) {
|
|
285
|
+
this.add('Identity', {
|
|
286
|
+
isTypeOnly: true,
|
|
287
|
+
moduleSpecifier: 'identity-type',
|
|
288
|
+
namedImports: [{
|
|
289
|
+
name: 'Identity'
|
|
290
|
+
}]
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if ([/\bDecimal\b/, /\bArray<Decimal>\b/].some(re => re.test(propertyType))) {
|
|
294
|
+
// TODO: Deprecated and should be removed
|
|
295
|
+
this.add('Decimal', '@prisma/client-runtime-utils');
|
|
296
|
+
}
|
|
297
|
+
if (/\bPrisma\./.test(propertyType)) {
|
|
298
|
+
this.add('Prisma', config.prismaClientImport);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
createFrom(args) {
|
|
302
|
+
const {
|
|
303
|
+
defaultImport,
|
|
304
|
+
from,
|
|
305
|
+
namedImport,
|
|
306
|
+
namespaceImport
|
|
307
|
+
} = args;
|
|
308
|
+
let name = args.name;
|
|
309
|
+
const value = {
|
|
310
|
+
defaultImport: undefined,
|
|
311
|
+
moduleSpecifier: from,
|
|
312
|
+
namedImports: [],
|
|
313
|
+
namespaceImport: undefined
|
|
314
|
+
};
|
|
315
|
+
if (namedImport === true && namespaceImport) {
|
|
316
|
+
value.namedImports = [{
|
|
317
|
+
name: namespaceImport
|
|
318
|
+
}];
|
|
319
|
+
name = namespaceImport;
|
|
320
|
+
} else if (defaultImport) {
|
|
321
|
+
value.defaultImport = defaultImport === true ? name : defaultImport;
|
|
322
|
+
name = value.defaultImport;
|
|
323
|
+
} else if (namespaceImport) {
|
|
324
|
+
value.namespaceImport = namespaceImport;
|
|
325
|
+
name = namespaceImport;
|
|
326
|
+
} else {
|
|
327
|
+
value.namedImports = [{
|
|
328
|
+
name
|
|
329
|
+
}];
|
|
330
|
+
}
|
|
331
|
+
this.add(name, value);
|
|
332
|
+
}
|
|
333
|
+
*toStatements() {
|
|
334
|
+
const iterator = this.values();
|
|
335
|
+
let result = iterator.next();
|
|
336
|
+
while (result.value) {
|
|
337
|
+
yield {
|
|
338
|
+
...result.value,
|
|
339
|
+
kind: StructureKind.ImportDeclaration
|
|
340
|
+
};
|
|
341
|
+
result = iterator.next();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function generateFiles(args) {
|
|
347
|
+
const {
|
|
348
|
+
config,
|
|
349
|
+
eventEmitter,
|
|
350
|
+
output,
|
|
351
|
+
project
|
|
352
|
+
} = args;
|
|
353
|
+
if (config.emitSingle) {
|
|
354
|
+
combineToSingle({
|
|
355
|
+
config,
|
|
356
|
+
output,
|
|
357
|
+
project
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (config.emitCompiled) {
|
|
361
|
+
project.compilerOptions.set({
|
|
362
|
+
declaration: true,
|
|
363
|
+
declarationDir: output,
|
|
364
|
+
emitDecoratorMetadata: false,
|
|
365
|
+
outDir: output,
|
|
366
|
+
rootDir: output,
|
|
367
|
+
skipLibCheck: true
|
|
368
|
+
});
|
|
369
|
+
const emitResult = await project.emit();
|
|
370
|
+
const errors = emitResult.getDiagnostics().map(d => String(d.getMessageText()));
|
|
371
|
+
if (errors.length > 0) {
|
|
372
|
+
eventEmitter.emitSync('Warning', errors);
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
await project.save();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function combineToSingle(args) {
|
|
379
|
+
const {
|
|
380
|
+
config,
|
|
381
|
+
output,
|
|
382
|
+
project
|
|
383
|
+
} = args;
|
|
384
|
+
const rootDirectory = project.getDirectory(output) || project.createDirectory(output);
|
|
385
|
+
const sourceFile = rootDirectory.getSourceFile('index.ts') || rootDirectory.createSourceFile('index.ts', undefined, {
|
|
386
|
+
overwrite: true
|
|
387
|
+
});
|
|
388
|
+
const statements = project.getSourceFiles().flatMap(s => {
|
|
389
|
+
if (s === sourceFile) {
|
|
390
|
+
return [];
|
|
391
|
+
}
|
|
392
|
+
const classDeclaration = s.getClass(() => true);
|
|
393
|
+
const statements = s.getStructure().statements;
|
|
394
|
+
// Reget decorator full name
|
|
395
|
+
// TODO: Check possible bug of ts-morph
|
|
396
|
+
if (Array.isArray(statements)) {
|
|
397
|
+
for (const statement of statements) {
|
|
398
|
+
if (!(typeof statement === 'object' && statement.kind === StructureKind.Class)) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
for (const property of statement.properties || []) {
|
|
402
|
+
for (const decorator of property.decorators || []) {
|
|
403
|
+
const fullName = classDeclaration?.getProperty(property.name)?.getDecorator(decorator.name)?.getFullName();
|
|
404
|
+
ok(fullName, `Cannot get full name of decorator of class ${statement.name}`);
|
|
405
|
+
decorator.name = fullName;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
project.removeSourceFile(s);
|
|
411
|
+
return statements;
|
|
412
|
+
});
|
|
413
|
+
const imports = new ImportDeclarationMap();
|
|
414
|
+
const enums = [];
|
|
415
|
+
const classes = [];
|
|
416
|
+
for (const statement of statements) {
|
|
417
|
+
if (typeof statement === 'string') {
|
|
418
|
+
if (statement.startsWith('registerEnumType')) {
|
|
419
|
+
enums.push(statement);
|
|
420
|
+
}
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
switch (statement.kind) {
|
|
424
|
+
case StructureKind.ImportDeclaration:
|
|
425
|
+
{
|
|
426
|
+
if (statement.moduleSpecifier.startsWith('.')) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
for (const namedImport of statement.namedImports) {
|
|
430
|
+
const name = namedImport.alias || namedImport.name;
|
|
431
|
+
if (statement.moduleSpecifier === 'identity-type') {
|
|
432
|
+
imports.add(name, statement);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
imports.add(name, statement.moduleSpecifier);
|
|
436
|
+
}
|
|
437
|
+
if (statement.defaultImport) {
|
|
438
|
+
imports.createFrom({
|
|
439
|
+
defaultImport: statement.defaultImport,
|
|
440
|
+
from: statement.moduleSpecifier,
|
|
441
|
+
name: statement.defaultImport
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (statement.namespaceImport) {
|
|
445
|
+
imports.createFrom({
|
|
446
|
+
from: statement.moduleSpecifier,
|
|
447
|
+
name: statement.namespaceImport,
|
|
448
|
+
namespaceImport: statement.namespaceImport
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
case StructureKind.Enum:
|
|
454
|
+
{
|
|
455
|
+
enums.unshift(statement);
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case StructureKind.Class:
|
|
459
|
+
{
|
|
460
|
+
classes.push(statement);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
for (const customImport of config.customImport) {
|
|
466
|
+
imports.createFrom(customImport);
|
|
467
|
+
}
|
|
468
|
+
sourceFile.set({
|
|
469
|
+
kind: StructureKind.SourceFile,
|
|
470
|
+
statements: [...imports.toStatements(), ...enums, ...classes]
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const extensions = new Set(['.js', '.mjs', '.ts', '.mts']);
|
|
475
|
+
function adjustModuleSpecifier(moduleSpecifier, importExtension) {
|
|
476
|
+
if (moduleSpecifier.startsWith('.')) {
|
|
477
|
+
let specifierWithoutExtension = moduleSpecifier;
|
|
478
|
+
const extension = path.extname(moduleSpecifier);
|
|
479
|
+
if (extensions.has(extension)) {
|
|
480
|
+
specifierWithoutExtension = moduleSpecifier.slice(0, -extension.length);
|
|
481
|
+
}
|
|
482
|
+
if (!importExtension) return specifierWithoutExtension;
|
|
483
|
+
if (extension !== `.${importExtension}`) {
|
|
484
|
+
return `${specifierWithoutExtension}.${importExtension}`;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return moduleSpecifier;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function fileTypeByLocation(fieldLocation) {
|
|
491
|
+
switch (fieldLocation) {
|
|
492
|
+
case 'inputObjectTypes':
|
|
493
|
+
{
|
|
494
|
+
return 'input';
|
|
495
|
+
}
|
|
496
|
+
case 'outputObjectTypes':
|
|
497
|
+
{
|
|
498
|
+
return 'output';
|
|
499
|
+
}
|
|
500
|
+
case 'enumTypes':
|
|
501
|
+
{
|
|
502
|
+
return 'enum';
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return 'object';
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function relativePath(from, to) {
|
|
509
|
+
if (!from.startsWith('/')) {
|
|
510
|
+
from = `/${from}`;
|
|
511
|
+
}
|
|
512
|
+
if (!to.startsWith('/')) {
|
|
513
|
+
to = `/${to}`;
|
|
514
|
+
}
|
|
515
|
+
let result = getRelativePath(from, to);
|
|
516
|
+
if (!result.startsWith('.')) {
|
|
517
|
+
result = `./${result}`;
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function getGraphqlImport(args) {
|
|
523
|
+
const {
|
|
524
|
+
config,
|
|
525
|
+
fileType,
|
|
526
|
+
getSourceFile,
|
|
527
|
+
isId,
|
|
528
|
+
location,
|
|
529
|
+
noTypeId,
|
|
530
|
+
sourceFile,
|
|
531
|
+
typeName
|
|
532
|
+
} = args;
|
|
533
|
+
const {
|
|
534
|
+
graphqlScalars,
|
|
535
|
+
importExtension
|
|
536
|
+
} = config;
|
|
537
|
+
if (location === 'scalar') {
|
|
538
|
+
if (isId && !noTypeId) {
|
|
539
|
+
return {
|
|
540
|
+
name: 'ID',
|
|
541
|
+
specifier: '@nestjs/graphql'
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const graphqlType = graphqlScalars[typeName];
|
|
545
|
+
if (graphqlType) {
|
|
546
|
+
return {
|
|
547
|
+
name: graphqlType.name,
|
|
548
|
+
specifier: graphqlType.specifier
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
switch (typeName) {
|
|
552
|
+
case 'Float':
|
|
553
|
+
case 'Int':
|
|
554
|
+
{
|
|
555
|
+
return {
|
|
556
|
+
name: typeName,
|
|
557
|
+
specifier: '@nestjs/graphql'
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
case 'DateTime':
|
|
561
|
+
{
|
|
562
|
+
return {
|
|
563
|
+
name: 'Date',
|
|
564
|
+
specifier: undefined
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
case 'true':
|
|
568
|
+
case 'Boolean':
|
|
569
|
+
{
|
|
570
|
+
return {
|
|
571
|
+
name: 'Boolean',
|
|
572
|
+
specifier: undefined
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
case 'Decimal':
|
|
576
|
+
{
|
|
577
|
+
return {
|
|
578
|
+
name: 'GraphQLDecimal',
|
|
579
|
+
specifier: 'prisma-graphql-type-decimal'
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
case 'Json':
|
|
583
|
+
{
|
|
584
|
+
return {
|
|
585
|
+
name: 'GraphQLJSON',
|
|
586
|
+
specifier: 'graphql-type-json'
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
name: 'String',
|
|
592
|
+
specifier: undefined
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
let sourceFileType = fileTypeByLocation(location);
|
|
596
|
+
if (sourceFileType === 'output' && fileType === 'model') {
|
|
597
|
+
sourceFileType = 'model';
|
|
598
|
+
}
|
|
599
|
+
const specifier = adjustModuleSpecifier(relativePath(sourceFile.getFilePath(), getSourceFile({
|
|
600
|
+
name: typeName,
|
|
601
|
+
type: sourceFileType
|
|
602
|
+
}).getFilePath()), importExtension);
|
|
603
|
+
return {
|
|
604
|
+
name: typeName,
|
|
605
|
+
specifier
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Find input type for graphql field decorator.
|
|
611
|
+
*/
|
|
612
|
+
function getGraphqlInputType(inputTypes, pattern) {
|
|
613
|
+
let result;
|
|
614
|
+
inputTypes = inputTypes.filter(t => !['null', 'Null'].includes(String(t.type)));
|
|
615
|
+
inputTypes = uniqWith(inputTypes, isEqual);
|
|
616
|
+
if (inputTypes.length === 1) {
|
|
617
|
+
return inputTypes[0];
|
|
618
|
+
}
|
|
619
|
+
const countTypes = countBy(inputTypes, x => x.location);
|
|
620
|
+
const isOneType = Object.keys(countTypes).length === 1;
|
|
621
|
+
if (isOneType) {
|
|
622
|
+
result = inputTypes.find(x => x.isList);
|
|
623
|
+
if (result) {
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (pattern) {
|
|
628
|
+
if (pattern.startsWith('matcher:') || pattern.startsWith('match:')) {
|
|
629
|
+
const {
|
|
630
|
+
1: patternValue
|
|
631
|
+
} = pattern.split(':', 2);
|
|
632
|
+
const isMatch = outmatch(patternValue, {
|
|
633
|
+
separator: false
|
|
634
|
+
});
|
|
635
|
+
result = inputTypes.find(x => isMatch(String(x.type)));
|
|
636
|
+
if (result) {
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
result = inputTypes.find(x => String(x.type).includes(pattern));
|
|
641
|
+
if (result) {
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
result = inputTypes.find(x => x.location === 'inputObjectTypes');
|
|
646
|
+
if (result) {
|
|
647
|
+
return result;
|
|
648
|
+
}
|
|
649
|
+
if (countTypes.enumTypes && countTypes.scalar && inputTypes.some(x => x.type === 'Json' && x.location === 'scalar')) {
|
|
650
|
+
result = inputTypes.find(x => x.type === 'Json' && x.location === 'scalar');
|
|
651
|
+
if (result) {
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if ((countTypes.scalar >= 1 || countTypes.enumTypes >= 1) && countTypes.fieldRefTypes === 1) {
|
|
656
|
+
result = inputTypes.find(x => (x.location === 'scalar' || x.location === 'enumTypes') && x.isList);
|
|
657
|
+
if (result) {
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
result = inputTypes.find(x => x.location === 'scalar' || x.location === 'enumTypes');
|
|
661
|
+
if (result) {
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
throw new TypeError(`Cannot get matching input type from ${inputTypes.map(x => x.type).join(', ') || 'zero length inputTypes'}`);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Returns typescript property type.
|
|
670
|
+
*/
|
|
671
|
+
function getPropertyType(args) {
|
|
672
|
+
const {
|
|
673
|
+
location,
|
|
674
|
+
type
|
|
675
|
+
} = args;
|
|
676
|
+
switch (type) {
|
|
677
|
+
case 'Float':
|
|
678
|
+
case 'Int':
|
|
679
|
+
{
|
|
680
|
+
return ['number'];
|
|
681
|
+
}
|
|
682
|
+
case 'String':
|
|
683
|
+
{
|
|
684
|
+
return ['string'];
|
|
685
|
+
}
|
|
686
|
+
case 'Boolean':
|
|
687
|
+
{
|
|
688
|
+
return ['boolean'];
|
|
689
|
+
}
|
|
690
|
+
case 'DateTime':
|
|
691
|
+
{
|
|
692
|
+
return ['Date', 'string'];
|
|
693
|
+
}
|
|
694
|
+
case 'Decimal':
|
|
695
|
+
{
|
|
696
|
+
return ['Decimal']; // TODO: Use Prisma.Decimal
|
|
697
|
+
}
|
|
698
|
+
case 'Json':
|
|
699
|
+
{
|
|
700
|
+
return ['any'];
|
|
701
|
+
}
|
|
702
|
+
case 'Null':
|
|
703
|
+
{
|
|
704
|
+
return ['null'];
|
|
705
|
+
}
|
|
706
|
+
case 'Bytes':
|
|
707
|
+
{
|
|
708
|
+
return ['Prisma.Bytes'];
|
|
709
|
+
}
|
|
710
|
+
case 'BigInt':
|
|
711
|
+
{
|
|
712
|
+
return ['bigint', 'number'];
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (['inputObjectTypes', 'outputObjectTypes'].includes(location)) {
|
|
716
|
+
return [type];
|
|
717
|
+
}
|
|
718
|
+
if (location === 'enumTypes') {
|
|
719
|
+
const enumType = '`${' + type + '}`';
|
|
720
|
+
return [enumType];
|
|
721
|
+
}
|
|
722
|
+
if (location === 'scalar') {
|
|
723
|
+
return [type];
|
|
724
|
+
}
|
|
725
|
+
return ['unknown'];
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function getWhereUniqueAtLeastKeys(model) {
|
|
729
|
+
const names = model.fields.filter(field => field.isUnique || field.isId).map(field => field.name);
|
|
730
|
+
if (model.primaryKey) {
|
|
731
|
+
names.push(createFieldName(model.primaryKey));
|
|
732
|
+
}
|
|
733
|
+
for (const uniqueIndex of model.uniqueIndexes) {
|
|
734
|
+
names.push(createFieldName(uniqueIndex));
|
|
735
|
+
}
|
|
736
|
+
return names;
|
|
737
|
+
}
|
|
738
|
+
function createFieldName(args) {
|
|
739
|
+
const {
|
|
740
|
+
fields,
|
|
741
|
+
name
|
|
742
|
+
} = args;
|
|
743
|
+
return name || fields.join('_');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function isWhereUniqueInputType(name) {
|
|
747
|
+
return name.endsWith('WhereUniqueInput');
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Get property structure (field) for class.
|
|
752
|
+
*/
|
|
753
|
+
function propertyStructure(args) {
|
|
754
|
+
const {
|
|
755
|
+
hasExclamationToken,
|
|
756
|
+
hasQuestionToken,
|
|
757
|
+
isList,
|
|
758
|
+
isNullable,
|
|
759
|
+
location,
|
|
760
|
+
name,
|
|
761
|
+
propertyType
|
|
762
|
+
} = args;
|
|
763
|
+
const type = createProperyType({
|
|
764
|
+
isList,
|
|
765
|
+
location,
|
|
766
|
+
propertyType
|
|
767
|
+
});
|
|
768
|
+
return {
|
|
769
|
+
decorators: [],
|
|
770
|
+
hasExclamationToken: hasExclamationToken ?? !isNullable,
|
|
771
|
+
hasQuestionToken: hasQuestionToken ?? isNullable,
|
|
772
|
+
kind: StructureKind.Property,
|
|
773
|
+
leadingTrivia: '\n',
|
|
774
|
+
name,
|
|
775
|
+
type
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function createProperyType(args) {
|
|
779
|
+
const {
|
|
780
|
+
isList,
|
|
781
|
+
location,
|
|
782
|
+
propertyType
|
|
783
|
+
} = args;
|
|
784
|
+
return propertyType.map(type => {
|
|
785
|
+
if (isList) return `Array<${type}>`;
|
|
786
|
+
if (type.startsWith('Prisma.')) return type;
|
|
787
|
+
if (type === 'null') return type;
|
|
788
|
+
if (['inputObjectTypes', 'outputObjectTypes'].includes(location)) {
|
|
789
|
+
return `Identity<${type}>`;
|
|
790
|
+
}
|
|
791
|
+
return type;
|
|
792
|
+
}).join(' | ');
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function inputType(args) {
|
|
796
|
+
const {
|
|
797
|
+
classDecoratorName,
|
|
798
|
+
classTransformerTypeModels,
|
|
799
|
+
config,
|
|
800
|
+
eventEmitter,
|
|
801
|
+
fieldSettings,
|
|
802
|
+
fileType,
|
|
803
|
+
getModelName,
|
|
804
|
+
getSourceFile,
|
|
805
|
+
inputType,
|
|
806
|
+
models,
|
|
807
|
+
removeTypes,
|
|
808
|
+
typeNames
|
|
809
|
+
} = args;
|
|
810
|
+
typeNames.add(inputType.name);
|
|
811
|
+
const importDeclarations = new ImportDeclarationMap();
|
|
812
|
+
const sourceFile = getSourceFile({
|
|
813
|
+
name: inputType.name,
|
|
814
|
+
type: fileType
|
|
815
|
+
});
|
|
816
|
+
const classStructure = {
|
|
817
|
+
decorators: [{
|
|
818
|
+
arguments: [],
|
|
819
|
+
name: classDecoratorName
|
|
820
|
+
}],
|
|
821
|
+
isExported: true,
|
|
822
|
+
kind: StructureKind.Class,
|
|
823
|
+
name: inputType.name,
|
|
824
|
+
properties: []
|
|
825
|
+
};
|
|
826
|
+
const modelName = getModelName(inputType.name) || '';
|
|
827
|
+
const model = models.get(modelName);
|
|
828
|
+
const modelFieldSettings = model && fieldSettings.get(model.name);
|
|
829
|
+
const moduleSpecifier = '@nestjs/graphql';
|
|
830
|
+
importDeclarations.set('Field', {
|
|
831
|
+
moduleSpecifier,
|
|
832
|
+
namedImports: [{
|
|
833
|
+
name: 'Field'
|
|
834
|
+
}]
|
|
835
|
+
}).set(classDecoratorName, {
|
|
836
|
+
moduleSpecifier,
|
|
837
|
+
namedImports: [{
|
|
838
|
+
name: classDecoratorName
|
|
839
|
+
}]
|
|
840
|
+
});
|
|
841
|
+
const useInputType = config.useInputType.find(x => inputType.name.includes(x.typeName));
|
|
842
|
+
const isWhereUnique = isWhereUniqueInputType(inputType.name);
|
|
843
|
+
|
|
844
|
+
// if (inputType.name.includes('DecimalNullableFilter')) {
|
|
845
|
+
// console.log({ importDeclarations, location, property, propertyType });
|
|
846
|
+
// }
|
|
847
|
+
|
|
848
|
+
for (const field of inputType.fields) {
|
|
849
|
+
field.inputTypes = field.inputTypes.filter(t => !removeTypes.has(String(t.type)));
|
|
850
|
+
eventEmitter.emitSync(BeforeGenerateField, field, args);
|
|
851
|
+
const {
|
|
852
|
+
inputTypes,
|
|
853
|
+
isRequired,
|
|
854
|
+
name
|
|
855
|
+
} = field;
|
|
856
|
+
if (inputTypes.length === 0) {
|
|
857
|
+
// No types
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
const usePattern = useInputType?.ALL || useInputType?.[name];
|
|
861
|
+
const graphqlInputType = getGraphqlInputType(inputTypes, usePattern);
|
|
862
|
+
const {
|
|
863
|
+
isList,
|
|
864
|
+
location,
|
|
865
|
+
type
|
|
866
|
+
} = graphqlInputType;
|
|
867
|
+
const typeName = String(type);
|
|
868
|
+
const settings = modelFieldSettings?.get(name);
|
|
869
|
+
const propertySettings = settings?.getPropertyType({
|
|
870
|
+
input: true,
|
|
871
|
+
name: inputType.name
|
|
872
|
+
});
|
|
873
|
+
const modelField = model?.fields.find(f => f.name === name);
|
|
874
|
+
const isCustomsApplicable = typeName === modelField?.type;
|
|
875
|
+
const atLeastKeys = model && getWhereUniqueAtLeastKeys(model);
|
|
876
|
+
const whereUniqueInputType = isWhereUniqueInputType(typeName) && atLeastKeys && `Prisma.AtLeast<${typeName}, ${atLeastKeys.map(name => `'${name}'`).join(' | ')}>`;
|
|
877
|
+
const propertyType = castArray(propertySettings?.name || whereUniqueInputType || getPropertyType({
|
|
878
|
+
location,
|
|
879
|
+
type: typeName
|
|
880
|
+
}));
|
|
881
|
+
const hasExclamationToken = Boolean(isWhereUnique && config.unsafeCompatibleWhereUniqueInput && atLeastKeys?.includes(name));
|
|
882
|
+
const property = propertyStructure({
|
|
883
|
+
hasExclamationToken: hasExclamationToken || undefined,
|
|
884
|
+
hasQuestionToken: hasExclamationToken ? false : undefined,
|
|
885
|
+
isList,
|
|
886
|
+
isNullable: !isRequired,
|
|
887
|
+
location,
|
|
888
|
+
name,
|
|
889
|
+
propertyType
|
|
890
|
+
});
|
|
891
|
+
classStructure.properties.push(property);
|
|
892
|
+
importDeclarations.create({
|
|
893
|
+
config,
|
|
894
|
+
propertySettings,
|
|
895
|
+
propertyType: property.type
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
// Get graphql type
|
|
899
|
+
let graphqlType;
|
|
900
|
+
const shouldHideField = settings?.shouldHideField({
|
|
901
|
+
input: true,
|
|
902
|
+
name: inputType.name
|
|
903
|
+
}) || config.decorate.some(d => d.name === 'HideField' && d.from === moduleSpecifier && d.isMatchField(name) && d.isMatchType(inputType.name));
|
|
904
|
+
const fieldType = settings?.getFieldType({
|
|
905
|
+
input: true,
|
|
906
|
+
name: inputType.name
|
|
907
|
+
});
|
|
908
|
+
if (fieldType && isCustomsApplicable && !shouldHideField) {
|
|
909
|
+
graphqlType = fieldType.name;
|
|
910
|
+
importDeclarations.createFrom({
|
|
911
|
+
...fieldType
|
|
912
|
+
});
|
|
913
|
+
} else {
|
|
914
|
+
// Import property type class
|
|
915
|
+
const graphqlImport = getGraphqlImport({
|
|
916
|
+
config,
|
|
917
|
+
getSourceFile,
|
|
918
|
+
location,
|
|
919
|
+
sourceFile,
|
|
920
|
+
typeName
|
|
921
|
+
});
|
|
922
|
+
graphqlType = graphqlImport.name;
|
|
923
|
+
let referenceName = propertyType[0];
|
|
924
|
+
if (location === 'enumTypes') {
|
|
925
|
+
referenceName = last(referenceName.split(' '));
|
|
926
|
+
}
|
|
927
|
+
if (graphqlImport.specifier && !importDeclarations.has(graphqlImport.name) && graphqlImport.name !== inputType.name
|
|
928
|
+
// ((graphqlImport.name !== inputType.name && !shouldHideField) ||
|
|
929
|
+
// (shouldHideField && referenceName === graphqlImport.name))
|
|
930
|
+
) {
|
|
931
|
+
importDeclarations.set(graphqlImport.name, {
|
|
932
|
+
moduleSpecifier: graphqlImport.specifier,
|
|
933
|
+
namedImports: [{
|
|
934
|
+
name: graphqlImport.name
|
|
935
|
+
}]
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
ok$1(property.decorators, 'property.decorators is undefined');
|
|
940
|
+
if (shouldHideField) {
|
|
941
|
+
importDeclarations.add('HideField', moduleSpecifier);
|
|
942
|
+
property.decorators.push({
|
|
943
|
+
arguments: [],
|
|
944
|
+
name: 'HideField'
|
|
945
|
+
});
|
|
946
|
+
} else {
|
|
947
|
+
// Generate `@Field()` decorator
|
|
948
|
+
property.decorators.push({
|
|
949
|
+
arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
|
|
950
|
+
...settings?.fieldArguments(),
|
|
951
|
+
nullable: !isRequired
|
|
952
|
+
})],
|
|
953
|
+
name: 'Field'
|
|
954
|
+
});
|
|
955
|
+
if (graphqlType === 'GraphQLDecimal') {
|
|
956
|
+
importDeclarations.add('transformToDecimal', 'prisma-graphql-type-decimal');
|
|
957
|
+
importDeclarations.add('Transform', 'class-transformer');
|
|
958
|
+
importDeclarations.add('Type', 'class-transformer');
|
|
959
|
+
property.decorators.push({
|
|
960
|
+
arguments: ['() => Object'],
|
|
961
|
+
name: 'Type'
|
|
962
|
+
}, {
|
|
963
|
+
arguments: ['transformToDecimal'],
|
|
964
|
+
name: 'Transform'
|
|
965
|
+
});
|
|
966
|
+
} else if (location === 'inputObjectTypes' && (modelField?.type === 'Decimal' || ['connect', 'connectOrCreate', 'create', 'createMany', 'data', 'delete', 'deleteMany', 'disconnect', 'set', 'update', 'updateMany', 'upsert', 'where'].includes(name) || classTransformerTypeModels.has(getModelName(graphqlType) || '') || modelField?.kind === 'object' && models.get(modelField.type) && models.get(modelField.type)?.fields.some(field => field.kind === 'object' && classTransformerTypeModels.has(field.type)))) {
|
|
967
|
+
importDeclarations.add('Type', 'class-transformer');
|
|
968
|
+
property.decorators.push({
|
|
969
|
+
arguments: [`() => ${graphqlType}`],
|
|
970
|
+
name: 'Type'
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
if (isCustomsApplicable) {
|
|
974
|
+
for (const options of settings || []) {
|
|
975
|
+
if ((options.kind === 'Decorator' && options.input && options.match?.(name)) ?? true) {
|
|
976
|
+
property.decorators.push({
|
|
977
|
+
arguments: options.arguments,
|
|
978
|
+
name: options.name
|
|
979
|
+
});
|
|
980
|
+
ok$1(options.from, "Missed 'from' part in configuration or field setting");
|
|
981
|
+
importDeclarations.createFrom(options);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
for (const decorate of config.decorate) {
|
|
986
|
+
if (decorate.isMatchField(name) && decorate.isMatchType(inputType.name)) {
|
|
987
|
+
property.decorators.push({
|
|
988
|
+
arguments: decorate.arguments?.map(x => pupa(x, {
|
|
989
|
+
propertyType
|
|
990
|
+
})),
|
|
991
|
+
name: decorate.name
|
|
992
|
+
});
|
|
993
|
+
importDeclarations.createFrom(decorate);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
eventEmitter.emitSync('ClassProperty', property, {
|
|
998
|
+
isList,
|
|
999
|
+
location,
|
|
1000
|
+
propertyType
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
sourceFile.set({
|
|
1004
|
+
statements: [...importDeclarations.toStatements(), classStructure]
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
class ObjectSettings extends Array {
|
|
1009
|
+
shouldHideField({
|
|
1010
|
+
input = false,
|
|
1011
|
+
name,
|
|
1012
|
+
output = false
|
|
1013
|
+
}) {
|
|
1014
|
+
const hideField = this.find(s => s.name === 'HideField');
|
|
1015
|
+
return Boolean(hideField?.input && input || hideField?.output && output || hideField?.match?.(name));
|
|
1016
|
+
}
|
|
1017
|
+
getFieldType({
|
|
1018
|
+
input,
|
|
1019
|
+
name,
|
|
1020
|
+
output
|
|
1021
|
+
}) {
|
|
1022
|
+
const fieldType = this.find(s => s.kind === 'FieldType');
|
|
1023
|
+
if (!fieldType) {
|
|
1024
|
+
return undefined;
|
|
1025
|
+
}
|
|
1026
|
+
if (fieldType.match) {
|
|
1027
|
+
return fieldType.match(name) ? fieldType : undefined;
|
|
1028
|
+
}
|
|
1029
|
+
if (input && !fieldType.input) {
|
|
1030
|
+
return undefined;
|
|
1031
|
+
}
|
|
1032
|
+
if (output && !fieldType.output) {
|
|
1033
|
+
return undefined;
|
|
1034
|
+
}
|
|
1035
|
+
return fieldType;
|
|
1036
|
+
}
|
|
1037
|
+
getPropertyType({
|
|
1038
|
+
input,
|
|
1039
|
+
name,
|
|
1040
|
+
output
|
|
1041
|
+
}) {
|
|
1042
|
+
const propertyType = this.find(s => s.kind === 'PropertyType');
|
|
1043
|
+
if (!propertyType) {
|
|
1044
|
+
return undefined;
|
|
1045
|
+
}
|
|
1046
|
+
if (propertyType.match) {
|
|
1047
|
+
return propertyType.match(name) ? propertyType : undefined;
|
|
1048
|
+
}
|
|
1049
|
+
if (input && !propertyType.input) {
|
|
1050
|
+
return undefined;
|
|
1051
|
+
}
|
|
1052
|
+
if (output && !propertyType.output) {
|
|
1053
|
+
return undefined;
|
|
1054
|
+
}
|
|
1055
|
+
return propertyType;
|
|
1056
|
+
}
|
|
1057
|
+
getObjectTypeArguments(options) {
|
|
1058
|
+
const objectTypeOptions = merge({}, options);
|
|
1059
|
+
const resultArguments = [objectTypeOptions];
|
|
1060
|
+
const objectType = this.find(s => s.kind === 'ObjectType');
|
|
1061
|
+
if (objectType && isObject(objectType.arguments)) {
|
|
1062
|
+
const name = objectType.arguments.name;
|
|
1063
|
+
merge(objectTypeOptions, omit(objectType.arguments, 'name'));
|
|
1064
|
+
if (name) {
|
|
1065
|
+
resultArguments.unshift(name);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return resultArguments.map(x => JSON5.stringify(x));
|
|
1069
|
+
}
|
|
1070
|
+
fieldArguments() {
|
|
1071
|
+
const item = this.find(item => item.kind === 'Field');
|
|
1072
|
+
if (item) {
|
|
1073
|
+
return item.arguments;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
function createObjectSettings(args) {
|
|
1078
|
+
const {
|
|
1079
|
+
config,
|
|
1080
|
+
text
|
|
1081
|
+
} = args;
|
|
1082
|
+
const result = new ObjectSettings();
|
|
1083
|
+
const textLines = text.split('\n');
|
|
1084
|
+
const documentationLines = [];
|
|
1085
|
+
let fieldElement = result.find(item => item.kind === 'Field');
|
|
1086
|
+
if (!fieldElement) {
|
|
1087
|
+
fieldElement = {
|
|
1088
|
+
arguments: {},
|
|
1089
|
+
kind: 'Field',
|
|
1090
|
+
name: ''
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
for (const line of textLines) {
|
|
1094
|
+
const match = /^@(?<name>\w+(\.(\w+))?)\((?<args>.*)\)/.exec(line);
|
|
1095
|
+
const {
|
|
1096
|
+
documentLine,
|
|
1097
|
+
element
|
|
1098
|
+
} = createSettingElement({
|
|
1099
|
+
config,
|
|
1100
|
+
fieldElement,
|
|
1101
|
+
line,
|
|
1102
|
+
match
|
|
1103
|
+
});
|
|
1104
|
+
if (element) {
|
|
1105
|
+
result.push(element);
|
|
1106
|
+
}
|
|
1107
|
+
if (documentLine) {
|
|
1108
|
+
documentationLines.push(line);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return {
|
|
1112
|
+
documentation: documentationLines.filter(Boolean).join('\n') || undefined,
|
|
1113
|
+
settings: result
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function createSettingElement({
|
|
1117
|
+
config,
|
|
1118
|
+
fieldElement,
|
|
1119
|
+
line,
|
|
1120
|
+
match
|
|
1121
|
+
}) {
|
|
1122
|
+
const result = {
|
|
1123
|
+
documentLine: '',
|
|
1124
|
+
element: undefined
|
|
1125
|
+
};
|
|
1126
|
+
if (line.startsWith('@deprecated')) {
|
|
1127
|
+
fieldElement.arguments['deprecationReason'] = trim(line.slice(11));
|
|
1128
|
+
result.element = fieldElement;
|
|
1129
|
+
return result;
|
|
1130
|
+
}
|
|
1131
|
+
if (line.startsWith('@complexity')) {
|
|
1132
|
+
let n = Number.parseInt(trim(line.slice(11)));
|
|
1133
|
+
if (n !== n || n < 1) n = 1;
|
|
1134
|
+
fieldElement.arguments['complexity'] = n;
|
|
1135
|
+
result.element = fieldElement;
|
|
1136
|
+
return result;
|
|
1137
|
+
}
|
|
1138
|
+
const name = match?.groups?.name;
|
|
1139
|
+
if (!(match && name)) {
|
|
1140
|
+
result.documentLine = line;
|
|
1141
|
+
return result;
|
|
1142
|
+
}
|
|
1143
|
+
const element = {
|
|
1144
|
+
arguments: [],
|
|
1145
|
+
from: '',
|
|
1146
|
+
input: false,
|
|
1147
|
+
kind: 'Decorator',
|
|
1148
|
+
model: false,
|
|
1149
|
+
name: '',
|
|
1150
|
+
output: false
|
|
1151
|
+
};
|
|
1152
|
+
result.element = element;
|
|
1153
|
+
if (name === 'TypeGraphQL.omit' || name === 'HideField') {
|
|
1154
|
+
Object.assign(element, hideFieldDecorator(match));
|
|
1155
|
+
return result;
|
|
1156
|
+
}
|
|
1157
|
+
if (['FieldType', 'PropertyType'].includes(name) && match.groups?.args) {
|
|
1158
|
+
const options = customType(match.groups.args);
|
|
1159
|
+
merge(element, options.namespace && config.fields[options.namespace], options, {
|
|
1160
|
+
kind: name
|
|
1161
|
+
});
|
|
1162
|
+
return result;
|
|
1163
|
+
}
|
|
1164
|
+
if (name === 'ObjectType' && match.groups?.args) {
|
|
1165
|
+
element.kind = 'ObjectType';
|
|
1166
|
+
const options = customType(match.groups.args);
|
|
1167
|
+
if (typeof options[0] === 'string' && options[0]) {
|
|
1168
|
+
options.name = options[0];
|
|
1169
|
+
}
|
|
1170
|
+
if (isObject(options[1])) {
|
|
1171
|
+
merge(options, options[1]);
|
|
1172
|
+
}
|
|
1173
|
+
element.arguments = {
|
|
1174
|
+
isAbstract: options.isAbstract,
|
|
1175
|
+
name: options.name
|
|
1176
|
+
};
|
|
1177
|
+
return result;
|
|
1178
|
+
}
|
|
1179
|
+
if (name === 'Directive' && match.groups?.args) {
|
|
1180
|
+
const options = customType(match.groups.args);
|
|
1181
|
+
merge(element, {
|
|
1182
|
+
from: '@nestjs/graphql',
|
|
1183
|
+
model: true
|
|
1184
|
+
}, options, {
|
|
1185
|
+
arguments: Array.isArray(options.arguments) ? options.arguments.map(s => JSON5.stringify(s)) : options.arguments,
|
|
1186
|
+
kind: 'Decorator',
|
|
1187
|
+
name,
|
|
1188
|
+
namespace: false
|
|
1189
|
+
});
|
|
1190
|
+
return result;
|
|
1191
|
+
}
|
|
1192
|
+
const namespace = getNamespace(name);
|
|
1193
|
+
element.namespaceImport = namespace;
|
|
1194
|
+
const options = {
|
|
1195
|
+
arguments: (match.groups?.args || '').split(',').map(s => trim(s)).filter(Boolean),
|
|
1196
|
+
name
|
|
1197
|
+
};
|
|
1198
|
+
merge(element, namespace && config.fields[namespace], options);
|
|
1199
|
+
return result;
|
|
1200
|
+
}
|
|
1201
|
+
function customType(args) {
|
|
1202
|
+
const result = {};
|
|
1203
|
+
let options = parseArgs(args);
|
|
1204
|
+
if (typeof options === 'string') {
|
|
1205
|
+
options = {
|
|
1206
|
+
name: options
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
Object.assign(result, options);
|
|
1210
|
+
const namespace = getNamespace(options.name);
|
|
1211
|
+
result.namespace = namespace;
|
|
1212
|
+
if (options.name?.includes('.')) {
|
|
1213
|
+
result.namespaceImport = namespace;
|
|
1214
|
+
}
|
|
1215
|
+
if (typeof options.match === 'string' || Array.isArray(options.match)) {
|
|
1216
|
+
result.match = outmatch(options.match, {
|
|
1217
|
+
separator: false
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
return result;
|
|
1221
|
+
}
|
|
1222
|
+
function hideFieldDecorator(match) {
|
|
1223
|
+
const result = {
|
|
1224
|
+
arguments: [],
|
|
1225
|
+
defaultImport: undefined,
|
|
1226
|
+
from: '@nestjs/graphql',
|
|
1227
|
+
match: undefined,
|
|
1228
|
+
name: 'HideField',
|
|
1229
|
+
namespaceImport: undefined
|
|
1230
|
+
};
|
|
1231
|
+
if (!match.groups?.args) {
|
|
1232
|
+
result.output = true;
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
if (match.groups.args.includes('{') && match.groups.args.includes('}')) {
|
|
1236
|
+
const options = parseArgs(match.groups.args);
|
|
1237
|
+
result.output = Boolean(options.output);
|
|
1238
|
+
result.input = Boolean(options.input);
|
|
1239
|
+
if (typeof options.match === 'string' || Array.isArray(options.match)) {
|
|
1240
|
+
result.match = outmatch(options.match, {
|
|
1241
|
+
separator: false
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
} else {
|
|
1245
|
+
if (/output:\s*true/.test(match.groups.args)) {
|
|
1246
|
+
result.output = true;
|
|
1247
|
+
}
|
|
1248
|
+
if (/input:\s*true/.test(match.groups.args)) {
|
|
1249
|
+
result.input = true;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return result;
|
|
1253
|
+
}
|
|
1254
|
+
function parseArgs(string) {
|
|
1255
|
+
try {
|
|
1256
|
+
return JSON5.parse(string);
|
|
1257
|
+
} catch {
|
|
1258
|
+
try {
|
|
1259
|
+
return JSON5.parse(`[${string}]`);
|
|
1260
|
+
} catch {
|
|
1261
|
+
throw new Error(`Failed to parse: ${string}`);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function getNamespace(name) {
|
|
1266
|
+
if (name === undefined) {
|
|
1267
|
+
return undefined;
|
|
1268
|
+
}
|
|
1269
|
+
let result = String(name);
|
|
1270
|
+
if (result.includes('.')) {
|
|
1271
|
+
[result] = result.split('.');
|
|
1272
|
+
}
|
|
1273
|
+
return result;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function modelData(model, args) {
|
|
1277
|
+
const {
|
|
1278
|
+
classTransformerTypeModels,
|
|
1279
|
+
config,
|
|
1280
|
+
fieldSettings,
|
|
1281
|
+
modelFields,
|
|
1282
|
+
modelNames,
|
|
1283
|
+
models
|
|
1284
|
+
} = args;
|
|
1285
|
+
modelNames.push(model.name);
|
|
1286
|
+
models.set(model.name, model);
|
|
1287
|
+
const modelFieldsValue = new Map();
|
|
1288
|
+
modelFields.set(model.name, modelFieldsValue);
|
|
1289
|
+
const fieldSettingsValue = new Map();
|
|
1290
|
+
fieldSettings.set(model.name, fieldSettingsValue);
|
|
1291
|
+
for (const field of model.fields) {
|
|
1292
|
+
if (field.documentation) {
|
|
1293
|
+
const {
|
|
1294
|
+
documentation,
|
|
1295
|
+
settings
|
|
1296
|
+
} = createObjectSettings({
|
|
1297
|
+
config,
|
|
1298
|
+
text: field.documentation
|
|
1299
|
+
});
|
|
1300
|
+
field.documentation = documentation;
|
|
1301
|
+
fieldSettingsValue.set(field.name, settings);
|
|
1302
|
+
}
|
|
1303
|
+
modelFieldsValue.set(field.name, field);
|
|
1304
|
+
}
|
|
1305
|
+
if (model.fields.some(field => field.type === 'Decimal')) {
|
|
1306
|
+
classTransformerTypeModels.add(model.name);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function createComment(documentation, settings) {
|
|
1311
|
+
const documentationLines = documentation.split('\n');
|
|
1312
|
+
const commentLines = ['/**'];
|
|
1313
|
+
for (const line of documentationLines) {
|
|
1314
|
+
commentLines.push(` * ${line}`);
|
|
1315
|
+
}
|
|
1316
|
+
const deprecationReason = settings?.fieldArguments()?.deprecationReason;
|
|
1317
|
+
if (deprecationReason) {
|
|
1318
|
+
commentLines.push(` * @deprecated ${deprecationReason}`);
|
|
1319
|
+
}
|
|
1320
|
+
commentLines.push(' */\n');
|
|
1321
|
+
return commentLines.join('\n');
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function getOutputTypeName(name) {
|
|
1325
|
+
return name.replace(/(?:OutputType|Output)$/, '');
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
const nestjsGraphql$1 = '@nestjs/graphql';
|
|
1329
|
+
function modelOutputType(outputType, args) {
|
|
1330
|
+
const {
|
|
1331
|
+
config,
|
|
1332
|
+
eventEmitter,
|
|
1333
|
+
fieldSettings,
|
|
1334
|
+
getSourceFile,
|
|
1335
|
+
modelFields,
|
|
1336
|
+
models
|
|
1337
|
+
} = args;
|
|
1338
|
+
if (isManyAndReturnOutputType(outputType.name)) return;
|
|
1339
|
+
const model = models.get(outputType.name);
|
|
1340
|
+
ok$1(model, `Cannot find model by name ${outputType.name}`);
|
|
1341
|
+
const sourceFile = getSourceFile({
|
|
1342
|
+
name: outputType.name,
|
|
1343
|
+
type: 'model'
|
|
1344
|
+
});
|
|
1345
|
+
const sourceFileStructure = sourceFile.getStructure();
|
|
1346
|
+
const exportDeclaration = getExportDeclaration$1(model.name, sourceFileStructure.statements);
|
|
1347
|
+
const importDeclarations = new ImportDeclarationMap();
|
|
1348
|
+
const classStructure = {
|
|
1349
|
+
decorators: [{
|
|
1350
|
+
arguments: [],
|
|
1351
|
+
name: 'ObjectType'
|
|
1352
|
+
}],
|
|
1353
|
+
isExported: true,
|
|
1354
|
+
kind: StructureKind.Class,
|
|
1355
|
+
name: outputType.name,
|
|
1356
|
+
properties: []
|
|
1357
|
+
};
|
|
1358
|
+
sourceFileStructure.statements.push(classStructure);
|
|
1359
|
+
ok$1(classStructure.decorators, 'classStructure.decorators is undefined');
|
|
1360
|
+
const decorator = classStructure.decorators.find(d => d.name === 'ObjectType');
|
|
1361
|
+
ok$1(decorator, 'ObjectType decorator not found');
|
|
1362
|
+
let modelSettings;
|
|
1363
|
+
// Get model settings from documentation
|
|
1364
|
+
if (model.documentation) {
|
|
1365
|
+
const objectTypeOptions = {};
|
|
1366
|
+
const {
|
|
1367
|
+
documentation,
|
|
1368
|
+
settings
|
|
1369
|
+
} = createObjectSettings({
|
|
1370
|
+
config,
|
|
1371
|
+
text: model.documentation
|
|
1372
|
+
});
|
|
1373
|
+
if (documentation) {
|
|
1374
|
+
if (!classStructure.leadingTrivia) {
|
|
1375
|
+
classStructure.leadingTrivia = createComment(documentation);
|
|
1376
|
+
}
|
|
1377
|
+
objectTypeOptions.description = documentation;
|
|
1378
|
+
}
|
|
1379
|
+
decorator.arguments = settings.getObjectTypeArguments(objectTypeOptions);
|
|
1380
|
+
modelSettings = settings;
|
|
1381
|
+
}
|
|
1382
|
+
importDeclarations.add('Field', nestjsGraphql$1);
|
|
1383
|
+
importDeclarations.add('ObjectType', nestjsGraphql$1);
|
|
1384
|
+
for (const field of outputType.fields) {
|
|
1385
|
+
if (config.omitModelsCount && field.name === '_count') continue;
|
|
1386
|
+
let fileType = 'model';
|
|
1387
|
+
const {
|
|
1388
|
+
isList,
|
|
1389
|
+
location,
|
|
1390
|
+
namespace,
|
|
1391
|
+
type
|
|
1392
|
+
} = field.outputType;
|
|
1393
|
+
let outputTypeName = String(type);
|
|
1394
|
+
if (namespace !== 'model') {
|
|
1395
|
+
fileType = 'output';
|
|
1396
|
+
outputTypeName = getOutputTypeName(outputTypeName);
|
|
1397
|
+
}
|
|
1398
|
+
const modelField = modelFields.get(model.name)?.get(field.name);
|
|
1399
|
+
const settings = fieldSettings.get(model.name)?.get(field.name);
|
|
1400
|
+
const fieldType = settings?.getFieldType({
|
|
1401
|
+
name: outputType.name,
|
|
1402
|
+
output: true
|
|
1403
|
+
});
|
|
1404
|
+
const propertySettings = settings?.getPropertyType({
|
|
1405
|
+
name: outputType.name,
|
|
1406
|
+
output: true
|
|
1407
|
+
});
|
|
1408
|
+
const propertyType = castArray(propertySettings?.name || getPropertyType({
|
|
1409
|
+
location,
|
|
1410
|
+
type: outputTypeName
|
|
1411
|
+
}));
|
|
1412
|
+
|
|
1413
|
+
// For model we keep only one type
|
|
1414
|
+
propertyType.splice(1);
|
|
1415
|
+
if (field.isNullable && !isList) {
|
|
1416
|
+
propertyType.push('null');
|
|
1417
|
+
}
|
|
1418
|
+
let graphqlType;
|
|
1419
|
+
if (fieldType) {
|
|
1420
|
+
graphqlType = fieldType.name;
|
|
1421
|
+
importDeclarations.createFrom({
|
|
1422
|
+
...fieldType
|
|
1423
|
+
});
|
|
1424
|
+
} else {
|
|
1425
|
+
const graphqlImport = getGraphqlImport({
|
|
1426
|
+
config,
|
|
1427
|
+
fileType,
|
|
1428
|
+
getSourceFile,
|
|
1429
|
+
isId: modelField?.isId,
|
|
1430
|
+
location,
|
|
1431
|
+
noTypeId: config.noTypeId,
|
|
1432
|
+
sourceFile,
|
|
1433
|
+
typeName: outputTypeName
|
|
1434
|
+
});
|
|
1435
|
+
graphqlType = graphqlImport.name;
|
|
1436
|
+
if (graphqlImport.name !== outputType.name && graphqlImport.specifier) {
|
|
1437
|
+
importDeclarations.add(graphqlImport.name, graphqlImport.specifier);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const property = propertyStructure({
|
|
1441
|
+
hasExclamationToken: true,
|
|
1442
|
+
hasQuestionToken: location === 'outputObjectTypes',
|
|
1443
|
+
isList,
|
|
1444
|
+
isNullable: field.isNullable,
|
|
1445
|
+
location,
|
|
1446
|
+
name: field.name,
|
|
1447
|
+
propertyType
|
|
1448
|
+
});
|
|
1449
|
+
if (typeof property.leadingTrivia === 'string' && modelField?.documentation) {
|
|
1450
|
+
property.leadingTrivia += createComment(modelField.documentation, settings);
|
|
1451
|
+
}
|
|
1452
|
+
classStructure.properties?.push(property);
|
|
1453
|
+
importDeclarations.create({
|
|
1454
|
+
config,
|
|
1455
|
+
propertySettings,
|
|
1456
|
+
propertyType: property.type
|
|
1457
|
+
});
|
|
1458
|
+
ok$1(property.decorators, 'property.decorators is undefined');
|
|
1459
|
+
const shouldHideField = settings?.shouldHideField({
|
|
1460
|
+
name: outputType.name,
|
|
1461
|
+
output: true
|
|
1462
|
+
}) || config.decorate.some(d => d.name === 'HideField' && d.from === '@nestjs/graphql' && d.isMatchField(field.name) && d.isMatchType(outputTypeName));
|
|
1463
|
+
if (shouldHideField) {
|
|
1464
|
+
importDeclarations.add('HideField', nestjsGraphql$1);
|
|
1465
|
+
property.decorators.push({
|
|
1466
|
+
arguments: [],
|
|
1467
|
+
name: 'HideField'
|
|
1468
|
+
});
|
|
1469
|
+
} else {
|
|
1470
|
+
// Generate `@Field()` decorator
|
|
1471
|
+
property.decorators.push({
|
|
1472
|
+
arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
|
|
1473
|
+
...settings?.fieldArguments(),
|
|
1474
|
+
defaultValue: ['number', 'string', 'boolean'].includes(typeof modelField?.default) ? modelField?.default : undefined,
|
|
1475
|
+
description: modelField?.documentation,
|
|
1476
|
+
nullable: Boolean(field.isNullable)
|
|
1477
|
+
})],
|
|
1478
|
+
name: 'Field'
|
|
1479
|
+
});
|
|
1480
|
+
for (const setting of settings || []) {
|
|
1481
|
+
if (shouldBeDecorated(setting) && (setting.match?.(field.name) ?? true)) {
|
|
1482
|
+
property.decorators.push({
|
|
1483
|
+
arguments: setting.arguments,
|
|
1484
|
+
name: setting.name
|
|
1485
|
+
});
|
|
1486
|
+
ok$1(setting.from, "Missed 'from' part in configuration or field setting");
|
|
1487
|
+
importDeclarations.createFrom(setting);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
for (const decorate of config.decorate) {
|
|
1491
|
+
if (decorate.isMatchField(field.name) && decorate.isMatchType(outputTypeName)) {
|
|
1492
|
+
property.decorators.push({
|
|
1493
|
+
arguments: decorate.arguments?.map(x => pupa(x, {
|
|
1494
|
+
propertyType
|
|
1495
|
+
})),
|
|
1496
|
+
name: decorate.name
|
|
1497
|
+
});
|
|
1498
|
+
importDeclarations.createFrom(decorate);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
eventEmitter.emitSync('ClassProperty', property, {
|
|
1503
|
+
isList,
|
|
1504
|
+
location,
|
|
1505
|
+
propertyType
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Generate class decorators from model settings
|
|
1510
|
+
for (const setting of modelSettings || []) {
|
|
1511
|
+
if (shouldBeDecorated(setting)) {
|
|
1512
|
+
classStructure.decorators.push({
|
|
1513
|
+
arguments: setting.arguments,
|
|
1514
|
+
name: setting.name
|
|
1515
|
+
});
|
|
1516
|
+
importDeclarations.createFrom(setting);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (exportDeclaration) {
|
|
1520
|
+
sourceFile.set({
|
|
1521
|
+
statements: [exportDeclaration, '\n', classStructure]
|
|
1522
|
+
});
|
|
1523
|
+
const classDeclaration = sourceFile.getClassOrThrow(model.name);
|
|
1524
|
+
const commentedText = classDeclaration.getText().split('\n').map(x => `// ${x}`);
|
|
1525
|
+
classDeclaration.remove();
|
|
1526
|
+
sourceFile.addStatements(['\n', ...commentedText]);
|
|
1527
|
+
} else {
|
|
1528
|
+
sourceFile.set({
|
|
1529
|
+
statements: [...importDeclarations.toStatements(), classStructure]
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
function shouldBeDecorated(setting) {
|
|
1534
|
+
return setting.kind === 'Decorator' && (setting.output || setting.model) && !(setting.output && setting.model);
|
|
1535
|
+
}
|
|
1536
|
+
function getExportDeclaration$1(name, statements) {
|
|
1537
|
+
return statements.find(structure => {
|
|
1538
|
+
return structure.kind === StructureKind.ExportDeclaration && structure.namedExports.some(o => (o.alias || o.name) === name);
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
function noAtomicOperations(eventEmitter) {
|
|
1543
|
+
eventEmitter.on('BeforeInputType', beforeInputType$1);
|
|
1544
|
+
eventEmitter.on('BeforeGenerateFiles', beforeGenerateFiles$1);
|
|
1545
|
+
}
|
|
1546
|
+
function beforeInputType$1(args) {
|
|
1547
|
+
const {
|
|
1548
|
+
getModelName,
|
|
1549
|
+
inputType
|
|
1550
|
+
} = args;
|
|
1551
|
+
for (const field of inputType.fields) {
|
|
1552
|
+
const fieldName = field.name;
|
|
1553
|
+
field.inputTypes = field.inputTypes.filter(inputType => {
|
|
1554
|
+
const inputTypeName = String(inputType.type);
|
|
1555
|
+
const modelName = getModelName(inputTypeName);
|
|
1556
|
+
if (isAtomicOperation(inputTypeName) || modelName && isListInput(inputTypeName, modelName, fieldName)) {
|
|
1557
|
+
return false;
|
|
1558
|
+
}
|
|
1559
|
+
return true;
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
function beforeGenerateFiles$1(args) {
|
|
1564
|
+
const {
|
|
1565
|
+
project
|
|
1566
|
+
} = args;
|
|
1567
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
1568
|
+
const className = sourceFile.getClass(() => true)?.getName();
|
|
1569
|
+
if (className && isAtomicOperation(className)) {
|
|
1570
|
+
project.removeSourceFile(sourceFile);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
function isAtomicOperation(typeName) {
|
|
1575
|
+
if (typeName.endsWith('FieldUpdateOperationsInput')) {
|
|
1576
|
+
return true;
|
|
1577
|
+
}
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
function isListInput(typeName, model, field) {
|
|
1581
|
+
return typeName === `${model}Create${field}Input` || typeName === `${model}Update${field}Input`;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function getEnumName(referenceName) {
|
|
1585
|
+
// `${Role}`
|
|
1586
|
+
return referenceName.slice(3, -2);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
const nestjsGraphql = '@nestjs/graphql';
|
|
1590
|
+
function outputType(outputType, args) {
|
|
1591
|
+
const {
|
|
1592
|
+
config,
|
|
1593
|
+
eventEmitter,
|
|
1594
|
+
fieldSettings,
|
|
1595
|
+
getModelName,
|
|
1596
|
+
getSourceFile,
|
|
1597
|
+
models
|
|
1598
|
+
} = args;
|
|
1599
|
+
const importDeclarations = new ImportDeclarationMap();
|
|
1600
|
+
const fileType = 'output';
|
|
1601
|
+
const modelName = getModelName(outputType.name) || '';
|
|
1602
|
+
const model = models.get(modelName);
|
|
1603
|
+
const isAggregateOutput = model && /(?:Count|Avg|Sum|Min|Max)AggregateOutputType$/.test(outputType.name) && String(outputType.name).startsWith(model.name);
|
|
1604
|
+
const isCountOutput = model?.name && outputType.name === `${model.name}CountOutputType`;
|
|
1605
|
+
if (!config.emitBlocks.outputs && !isCountOutput) return;
|
|
1606
|
+
|
|
1607
|
+
// Get rid of bogus suffixes
|
|
1608
|
+
outputType.name = getOutputTypeName(outputType.name);
|
|
1609
|
+
if (isAggregateOutput) {
|
|
1610
|
+
eventEmitter.emitSync('AggregateOutput', {
|
|
1611
|
+
...args,
|
|
1612
|
+
outputType
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
const sourceFile = getSourceFile({
|
|
1616
|
+
name: outputType.name,
|
|
1617
|
+
type: fileType
|
|
1618
|
+
});
|
|
1619
|
+
const classStructure = {
|
|
1620
|
+
decorators: [{
|
|
1621
|
+
arguments: [],
|
|
1622
|
+
name: 'ObjectType'
|
|
1623
|
+
}],
|
|
1624
|
+
isExported: true,
|
|
1625
|
+
kind: StructureKind.Class,
|
|
1626
|
+
name: outputType.name,
|
|
1627
|
+
properties: []
|
|
1628
|
+
};
|
|
1629
|
+
importDeclarations.add('Field', nestjsGraphql);
|
|
1630
|
+
importDeclarations.add('ObjectType', nestjsGraphql);
|
|
1631
|
+
for (const field of outputType.fields) {
|
|
1632
|
+
const {
|
|
1633
|
+
isList,
|
|
1634
|
+
location,
|
|
1635
|
+
type
|
|
1636
|
+
} = field.outputType;
|
|
1637
|
+
const outputTypeName = getOutputTypeName(String(type));
|
|
1638
|
+
const settings = isCountOutput ? undefined : model && fieldSettings.get(model.name)?.get(field.name);
|
|
1639
|
+
const propertySettings = settings?.getPropertyType({
|
|
1640
|
+
name: outputType.name,
|
|
1641
|
+
output: true
|
|
1642
|
+
});
|
|
1643
|
+
const isCustomsApplicable = outputTypeName === model?.fields.find(f => f.name === field.name)?.type;
|
|
1644
|
+
field.outputType.type = outputTypeName;
|
|
1645
|
+
const propertyType = castArray(propertySettings?.name || getPropertyType({
|
|
1646
|
+
location,
|
|
1647
|
+
type: outputTypeName
|
|
1648
|
+
}));
|
|
1649
|
+
const property = propertyStructure({
|
|
1650
|
+
hasQuestionToken: isCountOutput ? true : undefined,
|
|
1651
|
+
isList,
|
|
1652
|
+
isNullable: field.isNullable,
|
|
1653
|
+
location,
|
|
1654
|
+
name: field.name,
|
|
1655
|
+
propertyType
|
|
1656
|
+
});
|
|
1657
|
+
classStructure.properties?.push(property);
|
|
1658
|
+
importDeclarations.create({
|
|
1659
|
+
config,
|
|
1660
|
+
propertySettings,
|
|
1661
|
+
propertyType: property.type
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
// Get graphql type
|
|
1665
|
+
let graphqlType;
|
|
1666
|
+
const shouldHideField = settings?.shouldHideField({
|
|
1667
|
+
name: outputType.name,
|
|
1668
|
+
output: true
|
|
1669
|
+
}) || config.decorate.some(d => d.name === 'HideField' && d.from === '@nestjs/graphql' && d.isMatchField(field.name) && d.isMatchType(outputTypeName));
|
|
1670
|
+
const fieldType = settings?.getFieldType({
|
|
1671
|
+
name: outputType.name,
|
|
1672
|
+
output: true
|
|
1673
|
+
});
|
|
1674
|
+
if (fieldType && isCustomsApplicable && !shouldHideField) {
|
|
1675
|
+
graphqlType = fieldType.name;
|
|
1676
|
+
importDeclarations.createFrom({
|
|
1677
|
+
...fieldType
|
|
1678
|
+
});
|
|
1679
|
+
} else {
|
|
1680
|
+
const graphqlImport = getGraphqlImport({
|
|
1681
|
+
config,
|
|
1682
|
+
fileType,
|
|
1683
|
+
getSourceFile,
|
|
1684
|
+
isId: false,
|
|
1685
|
+
location,
|
|
1686
|
+
sourceFile,
|
|
1687
|
+
typeName: outputTypeName
|
|
1688
|
+
});
|
|
1689
|
+
const referenceName = location === 'enumTypes' ? getEnumName(propertyType[0]) : propertyType[0];
|
|
1690
|
+
graphqlType = graphqlImport.name;
|
|
1691
|
+
if (graphqlImport.specifier && !importDeclarations.has(graphqlImport.name) && (graphqlImport.name !== outputType.name && !shouldHideField || shouldHideField && referenceName === graphqlImport.name)) {
|
|
1692
|
+
importDeclarations.set(graphqlImport.name, {
|
|
1693
|
+
moduleSpecifier: graphqlImport.specifier,
|
|
1694
|
+
namedImports: [{
|
|
1695
|
+
name: graphqlImport.name
|
|
1696
|
+
}]
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
ok$1(property.decorators, 'property.decorators is undefined');
|
|
1701
|
+
if (shouldHideField) {
|
|
1702
|
+
importDeclarations.add('HideField', nestjsGraphql);
|
|
1703
|
+
property.decorators.push({
|
|
1704
|
+
arguments: [],
|
|
1705
|
+
name: 'HideField'
|
|
1706
|
+
});
|
|
1707
|
+
} else {
|
|
1708
|
+
// Generate `@Field()` decorator
|
|
1709
|
+
property.decorators.push({
|
|
1710
|
+
arguments: [isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`, JSON5.stringify({
|
|
1711
|
+
...settings?.fieldArguments(),
|
|
1712
|
+
nullable: Boolean(field.isNullable)
|
|
1713
|
+
})],
|
|
1714
|
+
name: 'Field'
|
|
1715
|
+
});
|
|
1716
|
+
if (isCustomsApplicable) {
|
|
1717
|
+
for (const options of settings || []) {
|
|
1718
|
+
if ((options.kind === 'Decorator' && options.output && options.match?.(field.name)) ?? true) {
|
|
1719
|
+
property.decorators.push({
|
|
1720
|
+
arguments: options.arguments,
|
|
1721
|
+
name: options.name
|
|
1722
|
+
});
|
|
1723
|
+
ok$1(options.from, "Missed 'from' part in configuration or field setting");
|
|
1724
|
+
importDeclarations.createFrom(options);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
eventEmitter.emitSync('ClassProperty', property, {
|
|
1730
|
+
isList,
|
|
1731
|
+
location,
|
|
1732
|
+
propertyType
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
sourceFile.set({
|
|
1736
|
+
statements: [...importDeclarations.toStatements(), classStructure]
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
function purgeOutput(emitter) {
|
|
1741
|
+
emitter.on('Begin', begin);
|
|
1742
|
+
emitter.on('End', end);
|
|
1743
|
+
}
|
|
1744
|
+
function begin({
|
|
1745
|
+
output,
|
|
1746
|
+
project
|
|
1747
|
+
}) {
|
|
1748
|
+
const sourceFiles = project.getDirectory(output)?.getDescendantSourceFiles();
|
|
1749
|
+
if (sourceFiles) {
|
|
1750
|
+
for (const sourceFile of sourceFiles) {
|
|
1751
|
+
sourceFile.delete();
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function end({
|
|
1756
|
+
output,
|
|
1757
|
+
project
|
|
1758
|
+
}) {
|
|
1759
|
+
const directories = project.getDirectory(output)?.getDescendantDirectories().filter(directory => directory.getSourceFiles().length === 0).map(directory => directory.getPath());
|
|
1760
|
+
for (const directory of directories || []) {
|
|
1761
|
+
try {
|
|
1762
|
+
fs.rmdirSync(directory);
|
|
1763
|
+
// eslint-disable-next-line no-empty
|
|
1764
|
+
} catch {}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
const ReExport = {
|
|
1769
|
+
All: 'All',
|
|
1770
|
+
Directories: 'Directories',
|
|
1771
|
+
None: 'None',
|
|
1772
|
+
Single: 'Single'
|
|
1773
|
+
};
|
|
1774
|
+
function reExport(emitter) {
|
|
1775
|
+
emitter.on('BeforeGenerateFiles', beforeGenerateFiles);
|
|
1776
|
+
}
|
|
1777
|
+
function beforeGenerateFiles(args) {
|
|
1778
|
+
const {
|
|
1779
|
+
config,
|
|
1780
|
+
output,
|
|
1781
|
+
project
|
|
1782
|
+
} = args;
|
|
1783
|
+
const rootDirectory = project.getDirectoryOrThrow(output);
|
|
1784
|
+
const {
|
|
1785
|
+
importExtension,
|
|
1786
|
+
reExport
|
|
1787
|
+
} = config;
|
|
1788
|
+
if ([ReExport.Directories, ReExport.All].includes(reExport)) {
|
|
1789
|
+
for (const directory of rootDirectory.getDescendantDirectories()) {
|
|
1790
|
+
let indexSourceFile;
|
|
1791
|
+
const exportDeclarations = directory.getSourceFiles().filter(sourceFile => {
|
|
1792
|
+
return sourceFile.getBaseName() !== 'index.ts';
|
|
1793
|
+
}).map(sourcesFile => getExportDeclaration(directory, sourcesFile, importExtension));
|
|
1794
|
+
if (exportDeclarations.length > 0) {
|
|
1795
|
+
indexSourceFile = directory.createSourceFile('index.ts', {
|
|
1796
|
+
statements: exportDeclarations
|
|
1797
|
+
}, {
|
|
1798
|
+
overwrite: true
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
if (indexSourceFile) {
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
const namespaceExportDeclarations = directory.getDirectories().map(sourceDirectory => getNamespaceExportDeclaration(directory, sourceDirectory, importExtension));
|
|
1805
|
+
project.createSourceFile(`${directory.getPath()}/index.ts`, {
|
|
1806
|
+
statements: namespaceExportDeclarations
|
|
1807
|
+
}, {
|
|
1808
|
+
overwrite: true
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
if (reExport === ReExport.Single) {
|
|
1813
|
+
const exportDeclarations = project.getSourceFiles().filter(sourceFile => {
|
|
1814
|
+
return sourceFile.getBaseName() !== 'index.ts';
|
|
1815
|
+
}).map(sourceFile => getExportDeclaration(rootDirectory, sourceFile, importExtension));
|
|
1816
|
+
rootDirectory.createSourceFile('index.ts', {
|
|
1817
|
+
statements: exportDeclarations
|
|
1818
|
+
}, {
|
|
1819
|
+
overwrite: true
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
if (reExport === ReExport.All) {
|
|
1823
|
+
const exportDeclarations = [];
|
|
1824
|
+
for (const directory of rootDirectory.getDirectories()) {
|
|
1825
|
+
if (directory.getBaseName() === 'node_modules') continue;
|
|
1826
|
+
const sourceFile = directory.getSourceFileOrThrow('index.ts');
|
|
1827
|
+
exportDeclarations.push(getExportDeclaration(rootDirectory, sourceFile, importExtension));
|
|
1828
|
+
}
|
|
1829
|
+
rootDirectory.createSourceFile('index.ts', {
|
|
1830
|
+
statements: exportDeclarations
|
|
1831
|
+
}, {
|
|
1832
|
+
overwrite: true
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
function getExportDeclaration(directory, sourceFile, importExtension) {
|
|
1837
|
+
const moduleSpecifier = adjustModuleSpecifier(directory.getRelativePathAsModuleSpecifierTo(sourceFile), importExtension);
|
|
1838
|
+
return {
|
|
1839
|
+
kind: StructureKind.ExportDeclaration,
|
|
1840
|
+
moduleSpecifier,
|
|
1841
|
+
namedExports: sourceFile.getExportSymbols().map(s => ({
|
|
1842
|
+
name: s.getName()
|
|
1843
|
+
}))
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
function getNamespaceExportDeclaration(directory, sourceDirectory, importExtension) {
|
|
1847
|
+
const moduleSpecifier = adjustModuleSpecifier(directory.getRelativePathAsModuleSpecifierTo(sourceDirectory), importExtension);
|
|
1848
|
+
return {
|
|
1849
|
+
kind: StructureKind.ExportDeclaration,
|
|
1850
|
+
moduleSpecifier
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
// utils/prisma-enum-doc.ts
|
|
1855
|
+
|
|
1856
|
+
function extractEnumValueDocs(values) {
|
|
1857
|
+
return Object.fromEntries(values.map(value => {
|
|
1858
|
+
const {
|
|
1859
|
+
name
|
|
1860
|
+
} = value;
|
|
1861
|
+
const documentation = value.documentation;
|
|
1862
|
+
if (typeof documentation !== 'string') return null;
|
|
1863
|
+
if (documentation.startsWith('@deprecated')) {
|
|
1864
|
+
return [name, {
|
|
1865
|
+
deprecationReason: documentation.slice(11).trim()
|
|
1866
|
+
}];
|
|
1867
|
+
}
|
|
1868
|
+
return [name, {
|
|
1869
|
+
description: documentation
|
|
1870
|
+
}];
|
|
1871
|
+
}).filter(entry => entry !== null));
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
function registerEnum(enumType, args) {
|
|
1875
|
+
const {
|
|
1876
|
+
config,
|
|
1877
|
+
enums,
|
|
1878
|
+
getSourceFile
|
|
1879
|
+
} = args;
|
|
1880
|
+
if (!config.emitBlocks.prismaEnums && !enums[enumType.name]) return;
|
|
1881
|
+
const dataModelEnum = enums[enumType.name];
|
|
1882
|
+
const enumTypesData = dataModelEnum?.values || [];
|
|
1883
|
+
const sourceFile = getSourceFile({
|
|
1884
|
+
name: enumType.name,
|
|
1885
|
+
type: 'enum'
|
|
1886
|
+
});
|
|
1887
|
+
const importDeclarations = new ImportDeclarationMap();
|
|
1888
|
+
importDeclarations.set('registerEnumType', {
|
|
1889
|
+
moduleSpecifier: '@nestjs/graphql',
|
|
1890
|
+
namedImports: [{
|
|
1891
|
+
name: 'registerEnumType'
|
|
1892
|
+
}]
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
// Extract valuesMap from enum documentation
|
|
1896
|
+
const valuesMap = extractEnumValueDocs(enumTypesData);
|
|
1897
|
+
|
|
1898
|
+
// Remove entries with no description or deprecationReason
|
|
1899
|
+
const filteredValuesMap = Object.fromEntries(Object.entries(valuesMap).filter(([_, v]) => Object.keys(v).length > 0));
|
|
1900
|
+
|
|
1901
|
+
// Format only if needed
|
|
1902
|
+
const hasValuesMap = Object.keys(filteredValuesMap).length > 0;
|
|
1903
|
+
const formattedValuesMap = hasValuesMap ? JSON.stringify(filteredValuesMap, null, 2).replaceAll(/"([^"]+)":/g, '$1:') : '';
|
|
1904
|
+
const valuesMapEntry = hasValuesMap ? `, valuesMap: ${formattedValuesMap}` : '';
|
|
1905
|
+
const enumStructure = {
|
|
1906
|
+
isExported: true,
|
|
1907
|
+
kind: StructureKind.Enum,
|
|
1908
|
+
members: enumType.values.map(v => ({
|
|
1909
|
+
initializer: JSON.stringify(v),
|
|
1910
|
+
name: v
|
|
1911
|
+
})),
|
|
1912
|
+
name: enumType.name
|
|
1913
|
+
};
|
|
1914
|
+
sourceFile.set({
|
|
1915
|
+
statements: [...importDeclarations.toStatements(), enumStructure, '\n', `registerEnumType(${enumType.name}, { name: '${enumType.name}', description: ${JSON.stringify(dataModelEnum?.documentation)}${valuesMapEntry} })`]
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
function requireSingleFieldsInWhereUniqueInput(eventEmitter) {
|
|
1920
|
+
eventEmitter.on('BeforeInputType', beforeInputType);
|
|
1921
|
+
}
|
|
1922
|
+
function beforeInputType(args) {
|
|
1923
|
+
const {
|
|
1924
|
+
inputType
|
|
1925
|
+
} = args;
|
|
1926
|
+
if (!isWhereUniqueInputType(inputType.name) || inputType.fields.length !== 1) {
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
for (const field of inputType.fields) {
|
|
1930
|
+
field.isRequired = true;
|
|
1931
|
+
field.isNullable = false;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
function warning(message) {
|
|
1936
|
+
if (Array.isArray(message)) {
|
|
1937
|
+
console.log('prisma-nestjs-graphql:');
|
|
1938
|
+
console.log(message.join('\n'));
|
|
1939
|
+
} else {
|
|
1940
|
+
console.log('prisma-nestjs-graphql:', message);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
const allEmmittedBlocks = ['prismaEnums', 'schemaEnums', 'models', 'inputs', 'args', 'outputs'];
|
|
1945
|
+
const blocksDependencyMap = {
|
|
1946
|
+
args: ['args', 'inputs', 'prismaEnums'],
|
|
1947
|
+
enums: ['schemaEnums', 'prismaEnums'],
|
|
1948
|
+
inputs: ['inputs', 'prismaEnums'],
|
|
1949
|
+
models: ['models', 'schemaEnums'],
|
|
1950
|
+
outputs: ['outputs']
|
|
1951
|
+
};
|
|
1952
|
+
function createEmitBlocks(data) {
|
|
1953
|
+
if (!data) {
|
|
1954
|
+
return Object.fromEntries(allEmmittedBlocks.map(block => [block, true]));
|
|
1955
|
+
}
|
|
1956
|
+
let blocksToEmit = {};
|
|
1957
|
+
for (const block of data) {
|
|
1958
|
+
if (!Object.keys(blocksDependencyMap).includes(block)) continue;
|
|
1959
|
+
blocksToEmit = {
|
|
1960
|
+
...blocksToEmit,
|
|
1961
|
+
...Object.fromEntries(blocksDependencyMap[block].map(block => [block, true]))
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
return blocksToEmit;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function createConfig(data) {
|
|
1968
|
+
const config = merge({}, unflatten(data, {
|
|
1969
|
+
delimiter: '_'
|
|
1970
|
+
}));
|
|
1971
|
+
const $warnings = [];
|
|
1972
|
+
const configOutputFilePattern = String(config.outputFilePattern || `{model}/{name}.{type}.ts`);
|
|
1973
|
+
const outputFilePattern = configOutputFilePattern.replaceAll('\\', '/').split('/').map(path => filenamify(path, {
|
|
1974
|
+
replacement: ''
|
|
1975
|
+
})).filter(Boolean).join('/');
|
|
1976
|
+
if (outputFilePattern !== configOutputFilePattern) {
|
|
1977
|
+
$warnings.push(`Due to invalid filepath 'outputFilePattern' changed to '${outputFilePattern}'`);
|
|
1978
|
+
}
|
|
1979
|
+
const fields = Object.fromEntries(Object.entries(config.fields ?? {}).filter(({
|
|
1980
|
+
1: value
|
|
1981
|
+
}) => typeof value === 'object').map(([name, value]) => {
|
|
1982
|
+
const fieldSetting = {
|
|
1983
|
+
arguments: [],
|
|
1984
|
+
defaultImport: toBoolean(value.defaultImport) ? true : value.defaultImport,
|
|
1985
|
+
from: value.from,
|
|
1986
|
+
input: toBoolean(value.input),
|
|
1987
|
+
model: toBoolean(value.model),
|
|
1988
|
+
namespaceImport: value.namespaceImport,
|
|
1989
|
+
output: toBoolean(value.output)
|
|
1990
|
+
};
|
|
1991
|
+
return [name, fieldSetting];
|
|
1992
|
+
}));
|
|
1993
|
+
const decorate = [];
|
|
1994
|
+
const configDecorate = Object.values(config.decorate || {});
|
|
1995
|
+
for (const element of configDecorate) {
|
|
1996
|
+
if (!element) continue;
|
|
1997
|
+
ok$1(element.from && element.name, `Missed 'from' or 'name' part in configuration for decorate`);
|
|
1998
|
+
decorate.push({
|
|
1999
|
+
arguments: element.arguments ? JSON5.parse(element.arguments) : undefined,
|
|
2000
|
+
defaultImport: toBoolean(element.defaultImport) ? true : element.defaultImport,
|
|
2001
|
+
from: element.from,
|
|
2002
|
+
isMatchField: outmatch(element.field, {
|
|
2003
|
+
separator: false
|
|
2004
|
+
}),
|
|
2005
|
+
isMatchType: outmatch(element.type, {
|
|
2006
|
+
separator: false
|
|
2007
|
+
}),
|
|
2008
|
+
name: element.name,
|
|
2009
|
+
namedImport: toBoolean(element.namedImport),
|
|
2010
|
+
namespaceImport: element.namespaceImport
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
const customImport = [];
|
|
2014
|
+
const configCustomImport = Object.values(config.customImport || {});
|
|
2015
|
+
for (const element of configCustomImport) {
|
|
2016
|
+
if (!element) continue;
|
|
2017
|
+
ok$1(element.from && element.name, `Missed 'from' or 'name' part in configuration for customImport`);
|
|
2018
|
+
customImport.push({
|
|
2019
|
+
defaultImport: toBoolean(element.defaultImport) ? true : element.defaultImport,
|
|
2020
|
+
from: element.from,
|
|
2021
|
+
name: element.name,
|
|
2022
|
+
namedImport: toBoolean(element.namedImport),
|
|
2023
|
+
namespaceImport: element.namespaceImport
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
return {
|
|
2027
|
+
$warnings,
|
|
2028
|
+
combineScalarFilters: toBoolean(config.combineScalarFilters),
|
|
2029
|
+
customImport,
|
|
2030
|
+
decorate,
|
|
2031
|
+
emitBlocks: createEmitBlocks(config.emitBlocks),
|
|
2032
|
+
emitCompiled: toBoolean(config.emitCompiled),
|
|
2033
|
+
emitSingle: toBoolean(config.emitSingle),
|
|
2034
|
+
fields,
|
|
2035
|
+
graphqlScalars: config.graphqlScalars || {},
|
|
2036
|
+
importExtension: String(config.importExtension ?? ''),
|
|
2037
|
+
noAtomicOperations: toBoolean(config.noAtomicOperations),
|
|
2038
|
+
noTypeId: toBoolean(config.noTypeId),
|
|
2039
|
+
omitModelsCount: toBoolean(config.omitModelsCount),
|
|
2040
|
+
outputFilePattern,
|
|
2041
|
+
prismaClientImport: createPrismaImport(config.prismaClientImport),
|
|
2042
|
+
purgeOutput: toBoolean(config.purgeOutput),
|
|
2043
|
+
reExport: ReExport[String(config.reExport)] || ReExport.None,
|
|
2044
|
+
requireSingleFieldsInWhereUniqueInput: toBoolean(config.requireSingleFieldsInWhereUniqueInput),
|
|
2045
|
+
tsConfigFilePath: createTsConfigFilePathValue(config.tsConfigFilePath),
|
|
2046
|
+
unsafeCompatibleWhereUniqueInput: toBoolean(config.unsafeCompatibleWhereUniqueInput),
|
|
2047
|
+
useInputType: createUseInputType(config.useInputType)
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
const tsConfigFileExists = memoize(filePath => {
|
|
2051
|
+
return fs.existsSync(filePath);
|
|
2052
|
+
});
|
|
2053
|
+
function createTsConfigFilePathValue(value) {
|
|
2054
|
+
if (typeof value === 'string') return value;
|
|
2055
|
+
if (tsConfigFileExists('tsconfig.json')) return 'tsconfig.json';
|
|
2056
|
+
}
|
|
2057
|
+
function createPrismaImport(value) {
|
|
2058
|
+
if (typeof value === 'string') return value;
|
|
2059
|
+
return '@prisma/client';
|
|
2060
|
+
}
|
|
2061
|
+
function createUseInputType(data) {
|
|
2062
|
+
if (!data) {
|
|
2063
|
+
return [];
|
|
2064
|
+
}
|
|
2065
|
+
const result = [];
|
|
2066
|
+
for (const [typeName, useInputs] of Object.entries(data)) {
|
|
2067
|
+
const entry = {
|
|
2068
|
+
ALL: undefined,
|
|
2069
|
+
typeName
|
|
2070
|
+
};
|
|
2071
|
+
if (useInputs.ALL) {
|
|
2072
|
+
entry.ALL = useInputs.ALL;
|
|
2073
|
+
delete useInputs.ALL;
|
|
2074
|
+
}
|
|
2075
|
+
for (const [propertyName, pattern] of Object.entries(useInputs)) {
|
|
2076
|
+
entry[propertyName] = pattern;
|
|
2077
|
+
}
|
|
2078
|
+
result.push(entry);
|
|
2079
|
+
}
|
|
2080
|
+
return result;
|
|
2081
|
+
}
|
|
2082
|
+
function toBoolean(value) {
|
|
2083
|
+
return ['true', '1', 'on'].includes(String(value));
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
function generateFileName(args) {
|
|
2087
|
+
const {
|
|
2088
|
+
getModelName,
|
|
2089
|
+
name,
|
|
2090
|
+
template,
|
|
2091
|
+
type
|
|
2092
|
+
} = args;
|
|
2093
|
+
return pupa(template, {
|
|
2094
|
+
get model() {
|
|
2095
|
+
const result = getModelName(name) || 'prisma';
|
|
2096
|
+
return kebabCase(result);
|
|
2097
|
+
},
|
|
2098
|
+
get name() {
|
|
2099
|
+
let result = kebabCase(name);
|
|
2100
|
+
for (const suffix of ['input', 'args', 'enum']) {
|
|
2101
|
+
const ending = `-${suffix}`;
|
|
2102
|
+
if (type === suffix && result.endsWith(ending)) {
|
|
2103
|
+
result = result.slice(0, -ending.length);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
return result;
|
|
2107
|
+
},
|
|
2108
|
+
plural: {
|
|
2109
|
+
get type() {
|
|
2110
|
+
return pluralize(type);
|
|
2111
|
+
}
|
|
2112
|
+
},
|
|
2113
|
+
type
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
function factoryGetSourceFile(args) {
|
|
2118
|
+
const {
|
|
2119
|
+
getModelName,
|
|
2120
|
+
output,
|
|
2121
|
+
outputFilePattern,
|
|
2122
|
+
project
|
|
2123
|
+
} = args;
|
|
2124
|
+
return function getSourceFile(args) {
|
|
2125
|
+
const {
|
|
2126
|
+
name,
|
|
2127
|
+
type
|
|
2128
|
+
} = args;
|
|
2129
|
+
let filePath = generateFileName({
|
|
2130
|
+
getModelName,
|
|
2131
|
+
name,
|
|
2132
|
+
template: outputFilePattern,
|
|
2133
|
+
type
|
|
2134
|
+
});
|
|
2135
|
+
filePath = `${output}/${filePath}`;
|
|
2136
|
+
return project.getSourceFile(filePath) || project.createSourceFile(filePath, undefined, {
|
|
2137
|
+
overwrite: true
|
|
2138
|
+
});
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
function createGetModelName(modelNames) {
|
|
2143
|
+
return memoize(tryGetName);
|
|
2144
|
+
function tryGetName(name) {
|
|
2145
|
+
return getModelName({
|
|
2146
|
+
modelNames,
|
|
2147
|
+
name
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
function getModelName(args) {
|
|
2152
|
+
const {
|
|
2153
|
+
modelNames,
|
|
2154
|
+
name
|
|
2155
|
+
} = args;
|
|
2156
|
+
for (const keyword of splitKeywords) {
|
|
2157
|
+
const [test] = name.split(keyword, 1);
|
|
2158
|
+
if (modelNames.includes(test)) {
|
|
2159
|
+
return test;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
for (const keyword of endsWithKeywords) {
|
|
2163
|
+
const [test] = name.split(keyword).slice(-1);
|
|
2164
|
+
if (modelNames.includes(test)) {
|
|
2165
|
+
return test;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
for (const [start, end] of middleKeywords) {
|
|
2169
|
+
let test = name.slice(start.length).slice(0, -end.length);
|
|
2170
|
+
if (modelNames.includes(test) && name.startsWith(start) && name.endsWith(end)) {
|
|
2171
|
+
return test;
|
|
2172
|
+
}
|
|
2173
|
+
test = name.slice(0, -(start + end).length);
|
|
2174
|
+
if (modelNames.includes(test) && name.endsWith(start + end)) {
|
|
2175
|
+
return test;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// test for {Model}{UniqueName}CompoundUniqueInput
|
|
2180
|
+
if (name.slice(-19) === 'CompoundUniqueInput') {
|
|
2181
|
+
const test = name.slice(0, -19);
|
|
2182
|
+
const models = modelNames.filter(x => test.startsWith(x)).sort((a, b) => b.length - a.length);
|
|
2183
|
+
return first(models);
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
// test for {Model}Count
|
|
2187
|
+
if (name.slice(-5) === 'Count') {
|
|
2188
|
+
const test = name.slice(0, -5);
|
|
2189
|
+
if (modelNames.includes(test)) {
|
|
2190
|
+
return test;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
const splitKeywords = ['CreateInput', 'CreateMany', 'CreateNested', 'CreateOneWithout', 'CreateOrConnect', 'CreateWithout', 'DistinctField', 'Filter', 'ManyWithout', 'OrderByInput', 'RelationFilter', 'NullableRelationFilter', 'ListRelationFilter', 'ScalarWhereInput', 'UpdateInput', 'UpdateMany', 'UpdateOneRequiredWithout', 'UpdateOneWithout', 'UpdateWith', 'UpsertWith', 'UpsertWithout', 'WhereInput', 'WhereUniqueInput', 'AvgAggregate', 'SumAggregate', 'MinAggregate', 'MaxAggregate', 'CountAggregate', 'ScalarField', 'GroupBy', 'OrderBy', 'UncheckedUpdate', 'UncheckedCreate', 'ScalarWhere', 'CountOutputType', 'CountOrderBy', 'SumOrderBy', 'MinOrderBy', 'MaxOrderBy', 'AvgOrderBy', 'Create', 'Update', 'ScalarRelationFilter', 'NullableScalarRelationFilter'].sort((a, b) => b.length - a.length);
|
|
2195
|
+
const endsWithKeywords = ['Aggregate', 'GroupBy', 'CreateOne', 'CreateMany', 'DeleteMany', 'DeleteOne', 'FindMany', 'FindOne', 'FindUnique', 'UpdateMany', 'UpdateOne', 'UpsertOne'];
|
|
2196
|
+
const middleKeywords = [['FindFirst', 'OrThrowArgs'], ['FindUnique', 'OrThrowArgs'], ['Aggregate', 'Args'], ['CreateOne', 'Args'], ['CreateMany', 'Args'], ['DeleteMany', 'Args'], ['DeleteOne', 'Args'], ['FindMany', 'Args'], ['FindFirst', 'Args'], ['FindOne', 'Args'], ['FindUnique', 'Args'], ['UpdateMany', 'Args'], ['UpdateMany', 'AndReturnOutputType'], ['UpdateOne', 'Args'], ['UpsertOne', 'Args'], ['GroupBy', 'Args'], ['OrderBy', 'Args']];
|
|
2197
|
+
|
|
2198
|
+
function resolveAwaitEventEmitter() {
|
|
2199
|
+
if (typeof awaitEventEmitterModule === 'function') return awaitEventEmitterModule;
|
|
2200
|
+
if (typeof awaitEventEmitterModule.default === 'function') return awaitEventEmitterModule.default;
|
|
2201
|
+
}
|
|
2202
|
+
async function generate(args) {
|
|
2203
|
+
const {
|
|
2204
|
+
connectCallback,
|
|
2205
|
+
dmmf,
|
|
2206
|
+
generator,
|
|
2207
|
+
skipAddOutputSourceFiles
|
|
2208
|
+
} = args;
|
|
2209
|
+
const generatorOutputValue = generator.output?.value;
|
|
2210
|
+
ok(generatorOutputValue, 'Missing generator configuration: output');
|
|
2211
|
+
const config = createConfig(generator.config);
|
|
2212
|
+
const AwaitEventEmitter = resolveAwaitEventEmitter();
|
|
2213
|
+
const eventEmitter = new AwaitEventEmitter();
|
|
2214
|
+
eventEmitter.on('Warning', warning);
|
|
2215
|
+
config.emitBlocks.models && eventEmitter.on('Model', modelData);
|
|
2216
|
+
if (config.emitBlocks.prismaEnums || config.emitBlocks.schemaEnums) {
|
|
2217
|
+
eventEmitter.on('EnumType', registerEnum);
|
|
2218
|
+
}
|
|
2219
|
+
if (config.emitBlocks.outputs || config.emitBlocks.models && !config.omitModelsCount) {
|
|
2220
|
+
eventEmitter.on('OutputType', outputType);
|
|
2221
|
+
}
|
|
2222
|
+
config.emitBlocks.models && eventEmitter.on('ModelOutputType', modelOutputType);
|
|
2223
|
+
config.emitBlocks.outputs && eventEmitter.on('AggregateOutput', createAggregateInput);
|
|
2224
|
+
config.emitBlocks.inputs && eventEmitter.on('InputType', inputType);
|
|
2225
|
+
config.emitBlocks.args && eventEmitter.on('ArgsType', argsType);
|
|
2226
|
+
eventEmitter.on('GenerateFiles', generateFiles);
|
|
2227
|
+
for (const message of config.$warnings) {
|
|
2228
|
+
eventEmitter.emitSync('Warning', message);
|
|
2229
|
+
}
|
|
2230
|
+
const project = new Project({
|
|
2231
|
+
manipulationSettings: {
|
|
2232
|
+
quoteKind: QuoteKind.Single
|
|
2233
|
+
},
|
|
2234
|
+
skipAddingFilesFromTsConfig: true,
|
|
2235
|
+
skipLoadingLibFiles: !config.emitCompiled,
|
|
2236
|
+
tsConfigFilePath: config.tsConfigFilePath
|
|
2237
|
+
});
|
|
2238
|
+
if (!skipAddOutputSourceFiles) {
|
|
2239
|
+
project.addSourceFilesAtPaths([`${generatorOutputValue}/**/*.ts`, `!${generatorOutputValue}/**/*.d.ts`]);
|
|
2240
|
+
}
|
|
2241
|
+
config.combineScalarFilters && combineScalarFilters(eventEmitter);
|
|
2242
|
+
config.noAtomicOperations && noAtomicOperations(eventEmitter);
|
|
2243
|
+
config.reExport !== ReExport.None && reExport(eventEmitter);
|
|
2244
|
+
config.purgeOutput && purgeOutput(eventEmitter);
|
|
2245
|
+
config.requireSingleFieldsInWhereUniqueInput && requireSingleFieldsInWhereUniqueInput(eventEmitter);
|
|
2246
|
+
const models = new Map();
|
|
2247
|
+
const modelNames = [];
|
|
2248
|
+
const modelFields = new Map();
|
|
2249
|
+
const fieldSettings = new Map();
|
|
2250
|
+
const getModelName = createGetModelName(modelNames);
|
|
2251
|
+
const getSourceFile = factoryGetSourceFile({
|
|
2252
|
+
eventEmitter,
|
|
2253
|
+
getModelName,
|
|
2254
|
+
output: generatorOutputValue,
|
|
2255
|
+
outputFilePattern: config.outputFilePattern,
|
|
2256
|
+
project
|
|
2257
|
+
});
|
|
2258
|
+
const {
|
|
2259
|
+
datamodel,
|
|
2260
|
+
schema
|
|
2261
|
+
} = structuredClone(dmmf);
|
|
2262
|
+
const removeTypes = new Set();
|
|
2263
|
+
const eventArguments = {
|
|
2264
|
+
classTransformerTypeModels: new Set(),
|
|
2265
|
+
config,
|
|
2266
|
+
enums: mapKeys(datamodel.enums, x => x.name),
|
|
2267
|
+
eventEmitter,
|
|
2268
|
+
fieldSettings,
|
|
2269
|
+
getModelName,
|
|
2270
|
+
getSourceFile,
|
|
2271
|
+
modelFields,
|
|
2272
|
+
modelNames,
|
|
2273
|
+
models,
|
|
2274
|
+
output: generatorOutputValue,
|
|
2275
|
+
project,
|
|
2276
|
+
removeTypes,
|
|
2277
|
+
schema,
|
|
2278
|
+
typeNames: new Set()
|
|
2279
|
+
};
|
|
2280
|
+
if (connectCallback) {
|
|
2281
|
+
await connectCallback(eventEmitter, eventArguments);
|
|
2282
|
+
}
|
|
2283
|
+
await eventEmitter.emit('Begin', eventArguments);
|
|
2284
|
+
for (const model of datamodel.models) {
|
|
2285
|
+
await eventEmitter.emit('Model', model, eventArguments);
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
// Types behaves like model
|
|
2289
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2290
|
+
for (const model of datamodel.types || []) {
|
|
2291
|
+
await eventEmitter.emit('Model', model, eventArguments);
|
|
2292
|
+
}
|
|
2293
|
+
const {
|
|
2294
|
+
enumTypes,
|
|
2295
|
+
inputObjectTypes,
|
|
2296
|
+
outputObjectTypes
|
|
2297
|
+
} = schema;
|
|
2298
|
+
await eventEmitter.emit('PostBegin', eventArguments);
|
|
2299
|
+
for (const enumType of enumTypes.prisma.concat(enumTypes.model || [])) {
|
|
2300
|
+
await eventEmitter.emit('EnumType', enumType, eventArguments);
|
|
2301
|
+
}
|
|
2302
|
+
for (const outputType of outputObjectTypes.model) {
|
|
2303
|
+
await eventEmitter.emit('ModelOutputType', outputType, eventArguments);
|
|
2304
|
+
}
|
|
2305
|
+
const queryOutputTypes = [];
|
|
2306
|
+
for (const outputType of outputObjectTypes.prisma) {
|
|
2307
|
+
if (['Query', 'Mutation'].includes(outputType.name)) {
|
|
2308
|
+
queryOutputTypes.push(outputType);
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
await eventEmitter.emit('OutputType', outputType, eventArguments);
|
|
2312
|
+
}
|
|
2313
|
+
const inputTypes = inputObjectTypes.prisma?.concat(inputObjectTypes.model ?? []) ?? [];
|
|
2314
|
+
for (const inputType of inputTypes) {
|
|
2315
|
+
const event = {
|
|
2316
|
+
...eventArguments,
|
|
2317
|
+
classDecoratorName: 'InputType',
|
|
2318
|
+
fileType: 'input',
|
|
2319
|
+
inputType
|
|
2320
|
+
};
|
|
2321
|
+
if (inputType.fields.length === 0) {
|
|
2322
|
+
removeTypes.add(inputType.name);
|
|
2323
|
+
continue;
|
|
2324
|
+
}
|
|
2325
|
+
await eventEmitter.emit('BeforeInputType', event);
|
|
2326
|
+
await eventEmitter.emit('InputType', event);
|
|
2327
|
+
}
|
|
2328
|
+
for (const outputType of queryOutputTypes) {
|
|
2329
|
+
for (const field of outputType.fields) {
|
|
2330
|
+
await eventEmitter.emit('ArgsType', field, eventArguments);
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
await eventEmitter.emit('BeforeGenerateFiles', eventArguments);
|
|
2334
|
+
await eventEmitter.emit('GenerateFiles', eventArguments);
|
|
2335
|
+
await eventEmitter.emit('End', eventArguments);
|
|
2336
|
+
for (const name of Object.keys(eventEmitter._events)) {
|
|
2337
|
+
eventEmitter.off(name);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
const generatorHandlerConfig = {
|
|
2341
|
+
async onGenerate(options) {
|
|
2342
|
+
await generate(options);
|
|
2343
|
+
},
|
|
2344
|
+
onManifest() {
|
|
2345
|
+
return {
|
|
2346
|
+
defaultOutput: '.',
|
|
2347
|
+
prettyName: 'Prisma NestJS/GraphQL'
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
|
|
2352
|
+
export { generate, generatorHandlerConfig };
|