Skip to content

Commit 4a7313d

Browse files
committed
feat: @PropertyType() to replace types_ configuration
1 parent 4ce28f1 commit 4a7313d

File tree

11 files changed

+311
-57
lines changed

11 files changed

+311
-57
lines changed

README.md

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ generator nestgraphql {
206206
fields_{Namespace}_output = true | false
207207
fields_{Namespace}_defaultImport = "default import name" | true
208208
fields_{Namespace}_namespaceImport = "namespace import name"
209+
fields_{Namespace}_namedImport = true | false
209210
}
210211
```
211212

@@ -214,9 +215,8 @@ Where `{Namespace}` is a namespace used in field triple slash comment.
214215

215216
##### `fields_{Namespace}_from`
216217

217-
Name of the module, which will be used in import (`class-validator`, `graphql-scalars`, etc.)
218-
Type: `string`
219-
Required: yes
218+
Required. Name of the module, which will be used in import (`class-validator`, `graphql-scalars`, etc.)
219+
Type: `string`
220220

221221
##### `fields_{Namespace}_input`
222222

@@ -243,7 +243,14 @@ Import all as this namespace from module
243243
Type: `undefined | string`
244244
Default: Equals to `{Namespace}`
245245

246-
Example:
246+
##### `fields_{Namespace}_namedImport`
247+
248+
If imported module has internal namespace, this allow to generate named import,
249+
imported name will be equal to `{Namespace}`, see [example of usage](#propertytype)
250+
Type: `boolean`
251+
Default: `false`
252+
253+
Custom decorators example:
247254

248255
```prisma
249256
generator nestgraphql {
@@ -327,6 +334,54 @@ model User {
327334
The result will be the same. `Scalars` is the namespace here.
328335
Missing field options will merged from generator configuration.
329336

337+
##### @PropertyType()
338+
339+
Similar to `@FieldType()` but refer to TypeScript property (actually field too).
340+
341+
Named import example:
342+
343+
```prisma
344+
model Transfer {
345+
id String @id
346+
/// @PropertyType({ name: 'Prisma.Decimal', from: '@prisma/client', namedImport: true })
347+
money Decimal
348+
}
349+
```
350+
351+
May generate following:
352+
353+
```ts
354+
import { Prisma } from '@prisma/client';
355+
356+
@ObjectType()
357+
export class User {
358+
@Field(() => GraphQLDecimal)
359+
money!: Prisma.Decimal;
360+
}
361+
```
362+
363+
Another example:
364+
365+
```
366+
model User {
367+
id String @id
368+
/// @PropertyType('TF.JsonObject')
369+
data Json
370+
}
371+
```
372+
373+
May generate:
374+
375+
```ts
376+
import * as TF from 'type-fest';
377+
378+
@ObjectType()
379+
export class User {
380+
@Field(() => GraphQLJSON)
381+
data!: TF.JsonObject;
382+
}
383+
```
384+
330385
## Similar Projects
331386

332387
- https://github.com/wSedlacek/prisma-generators/tree/master/libs/nestjs

src/@generated/dummy/dummy-max-order-by-aggregate.input.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export class DummyMaxOrderByAggregateInput {
77
@Field(() => SortOrder, { nullable: true })
88
id?: SortOrder;
99

10+
@Field(() => SortOrder, { nullable: true })
11+
created?: SortOrder;
12+
1013
@Field(() => SortOrder, { nullable: true })
1114
floaty?: SortOrder;
1215

src/handlers/input-type.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'assert';
22
import JSON5 from 'json5';
3+
import { castArray, trim } from 'lodash';
34
import { ClassDeclarationStructure, StructureKind } from 'ts-morph';
45

56
import { getGraphqlImport } from '../helpers/get-graphql-import';
@@ -65,13 +66,19 @@ export function inputType(
6566
const graphqlInputType = getGraphqlInputType(inputTypes);
6667
const { isList, location, type } = graphqlInputType;
6768
const typeName = String(type);
69+
// todo: remove
6870
const customType = config.types[typeName];
6971
const settings = modelFieldSettings?.get(field.name);
72+
const propertySettings = settings?.getPropertyType();
7073

71-
const propertyType = getPropertyType({
72-
location,
73-
type: typeName,
74-
});
74+
const propertyType = castArray(
75+
propertySettings?.name ||
76+
customType?.fieldType?.split('|').map(trim) ||
77+
getPropertyType({
78+
location,
79+
type: typeName,
80+
}),
81+
);
7582

7683
const property = propertyStructure({
7784
name: field.name,
@@ -82,6 +89,10 @@ export function inputType(
8289

8390
classStructure.properties?.push(property);
8491

92+
if (propertySettings) {
93+
importDeclarations.create({ ...propertySettings });
94+
}
95+
8596
let graphqlType: string;
8697
const fieldType = settings?.getFieldType();
8798

@@ -141,7 +152,7 @@ export function inputType(
141152
});
142153

143154
for (const options of settings || []) {
144-
if (!options.input || options.isFieldType) {
155+
if (!options.input || options.kind !== 'Decorator') {
145156
continue;
146157
}
147158
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

src/handlers/model-output-type.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import assert from 'assert';
22
import JSON5 from 'json5';
3-
import { remove } from 'lodash';
3+
import { castArray, remove, trim } from 'lodash';
44
import {
55
ClassDeclarationStructure,
66
CommentStatement,
@@ -99,16 +99,27 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
9999
fileType = 'output';
100100
outputTypeName = getOutputTypeName(outputTypeName);
101101
}
102-
const customType = config.types[outputTypeName];
102+
const customType = config.types[outputTypeName]; // todo: remove
103103
const modelField = modelFields.get(model.name)?.get(field.name);
104104
const settings = fieldSettings.get(model.name)?.get(field.name);
105+
const fieldType = settings?.getFieldType();
106+
const propertySettings = settings?.getPropertyType();
107+
108+
const propertyType = castArray(
109+
propertySettings?.name ||
110+
customType?.fieldType?.split('|').map(trim) ||
111+
getPropertyType({
112+
location,
113+
type: outputTypeName,
114+
}),
115+
);
116+
117+
// For model we keep only one type
118+
propertyType.splice(1, propertyType.length);
105119

106-
const propertyType = customType?.fieldType
107-
? [customType.fieldType]
108-
: getPropertyType({
109-
location,
110-
type: outputTypeName,
111-
});
120+
if (field.isNullable && !isList && ['enumTypes', 'scalar'].includes(location)) {
121+
propertyType.push('null');
122+
}
112123

113124
// For model we keep only one type
114125
propertyType.splice(1, propertyType.length);
@@ -118,7 +129,6 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
118129
}
119130

120131
let graphqlType: string;
121-
const fieldType = settings?.getFieldType();
122132

123133
if (fieldType) {
124134
graphqlType = fieldType.name;
@@ -164,6 +174,10 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
164174

165175
classStructure.properties?.push(property);
166176

177+
if (propertySettings) {
178+
importDeclarations.create({ ...propertySettings });
179+
}
180+
167181
// Create import for typescript field/property type
168182
if (customType && customType.fieldType && customType.fieldModule) {
169183
importDeclarations.add(customType.fieldType, customType.fieldModule);
@@ -190,7 +204,7 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
190204
});
191205

192206
for (const options of settings || []) {
193-
if (!options.output || options.isFieldType) {
207+
if (!options.output || options.kind !== 'Decorator') {
194208
continue;
195209
}
196210
property.decorators?.push({

src/handlers/output-type.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import assert from 'assert';
12
import JSON5 from 'json5';
3+
import { castArray, trim } from 'lodash';
24
import { ClassDeclarationStructure, StructureKind } from 'ts-morph';
35

46
import { getGraphqlImport } from '../helpers/get-graphql-import';
@@ -60,6 +62,8 @@ export function outputType(outputType: OutputType, args: EventArguments) {
6062
const { location, isList, type } = field.outputType;
6163
const outputTypeName = getOutputTypeName(String(type));
6264
const settings = model && fieldSettings.get(model.name)?.get(field.name);
65+
const propertySettings = settings?.getPropertyType();
66+
// todo: remove
6367
const customType = config.types[outputTypeName];
6468

6569
// console.log({
@@ -72,12 +76,14 @@ export function outputType(outputType: OutputType, args: EventArguments) {
7276

7377
field.outputType.type = outputTypeName;
7478

75-
const propertyType = customType?.fieldType
76-
? [customType.fieldType]
77-
: getPropertyType({
78-
location,
79-
type: outputTypeName,
80-
});
79+
const propertyType = castArray(
80+
propertySettings?.name ||
81+
customType?.fieldType?.split('|').map(trim) ||
82+
getPropertyType({
83+
location,
84+
type: outputTypeName,
85+
}),
86+
);
8187

8288
const property = propertyStructure({
8389
name: field.name,
@@ -88,6 +94,10 @@ export function outputType(outputType: OutputType, args: EventArguments) {
8894

8995
classStructure.properties?.push(property);
9096

97+
if (propertySettings) {
98+
importDeclarations.create({ ...propertySettings });
99+
}
100+
91101
const graphqlImport = getGraphqlImport({
92102
sourceFile,
93103
fileType,
@@ -123,6 +133,21 @@ export function outputType(outputType: OutputType, args: EventArguments) {
123133
}),
124134
],
125135
});
136+
137+
for (const options of settings || []) {
138+
if (!options.output || options.kind !== 'Decorator') {
139+
continue;
140+
}
141+
property.decorators?.push({
142+
name: options.name,
143+
arguments: options.arguments,
144+
});
145+
assert(
146+
options.from,
147+
"Missed 'from' part in configuration or field setting",
148+
);
149+
importDeclarations.create(options);
150+
}
126151
}
127152

128153
eventEmitter.emitSync('ClassProperty', property, { location, isList });

src/helpers/create-config.spec.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,6 @@ describe('createConfig', () => {
1616
expect(result.reExport).toEqual(ReExport.None);
1717
});
1818

19-
it('createConfig types', () => {
20-
const result = createConfig({
21-
types_Decimal_fieldType: `MyDec`,
22-
types_Decimal_fieldModule: `decimal.js`,
23-
});
24-
expect(result.types['Decimal']).toBeTruthy();
25-
expect(result.types['Decimal']?.fieldType).toEqual('MyDec');
26-
expect(result.types['Decimal']?.fieldModule).toEqual('decimal.js');
27-
expect(result.$warnings).toEqual([]);
28-
});
29-
3019
it('filename with parent reference should be not valid', () => {
3120
const result = createConfig({
3221
outputFilePattern: '../../../{model}//{name}.{type}.ts/',

src/helpers/create-config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export function createConfig(data: Record<string, string | undefined>) {
6161
}),
6262
);
6363

64+
if (Object.keys(types).length > 0) {
65+
$warnings.push(
66+
'Configuration throu `types_*` is deprecated, use @FieldType/@PropertyType https://github.com/unlight/prisma-nestjs-graphql#field-settings',
67+
);
68+
}
69+
6470
return {
6571
outputFilePattern,
6672
tsConfigFilePath: 'tsconfig.json' as string,

0 commit comments

Comments
 (0)