Skip to content

Commit 3319ff9

Browse files
committed
feat: Option to disable atomic number operations
1 parent 207785f commit 3319ff9

File tree

10 files changed

+234
-97
lines changed

10 files changed

+234
-97
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
Generate object types, inputs, args, etc. from prisma schema file for usage with @nestjs/graphql module.
44

5+
## Features
6+
7+
- Generates only necessary imports
8+
- Combines zoo of nested/nullable filters
9+
- Updates source code of existing files
10+
- Do not generate resolvers, since it's application specific
11+
512
## Install
613

714
```sh
@@ -34,6 +41,10 @@ npx prisma generate
3441
- `{name}` - name of model/input/arg
3542
- `{dasherizedName}` - dashed-case name of model/input/arg without suffix
3643
- `{type}` - short type name (model, input)
44+
- `combineScalarFilters` - Combine nested/nullable scalar filters to single
45+
(default: `true`)
46+
- `atomicNumberOperations` - Atomic number operations,
47+
`false` - disabled (default), `true` - enabled
3748

3849
## Resources
3950

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
}
4949
},
5050
"dependencies": {
51-
"@prisma/generator-helper": "^2.6.1",
51+
"@prisma/generator-helper": "^2.6.2",
52+
"boolean": "^3.0.1",
5253
"get-relative-path": "^1.0.2",
5354
"pupa": "^2.0.1",
5455
"to-kebab": "^1.0.7",
@@ -66,12 +67,12 @@
6667
"@nestjs/graphql": "^7.6.0",
6768
"@nestjs/platform-express": "^7.4.4",
6869
"@paljs/plugins": "^1.2.6",
69-
"@prisma/cli": "^2.6.1",
70+
"@prisma/cli": "^2.6.2",
7071
"@prisma/client": ">=2.6",
7172
"@semantic-release/changelog": "^5.0.1",
7273
"@semantic-release/git": "^9.0.0",
7374
"@types/mocha": "^8.0.3",
74-
"@types/node": "^14.6.3",
75+
"@types/node": "^14.6.4",
7576
"@typescript-eslint/eslint-plugin": "^4.0.1",
7677
"@typescript-eslint/parser": "^4.0.1",
7778
"apollo-server-express": "^2.17.0",

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ datasource database {
55

66
generator client {
77
provider = "prisma-client-js"
8+
previewFeatures = ["atomicNumberOperations"]
89
}
910

1011
generator nestgraphql {

src/generate-input.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Project, QuoteKind, SourceFile } from 'ts-morph';
33

44
import { generateInput } from './generate-input';
55
import { generatorOptions, stringContains, stringNotContains } from './testing';
6-
import { schemaOutputToInput } from './type-utils';
76

87
describe('generate inputs', () => {
98
let sourceFile: SourceFile;

src/generate-input.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export function generateInput(args: GenerateInputArgs) {
2727
});
2828
generateGraphqlImport({ name: 'Field', sourceFile, moduleSpecifier: '@nestjs/graphql' });
2929
for (const field of inputType.fields) {
30-
// console.log('field', field);
3130
const inputType = getMatchingInputType(field.inputType);
3231
const modelField = model?.fields.find((f) => f.name === field.name);
3332
const nullable =

src/generate.spec.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from 'assert';
2-
import { SourceFile } from 'ts-morph';
2+
import { PropertyDeclaration, SourceFile } from 'ts-morph';
33

44
import { generate } from './generate';
55
import { generatorOptions, stringContains } from './testing';
@@ -172,4 +172,64 @@ describe('main generate', () => {
172172
const struct = decorator.getStructure();
173173
assert.strictEqual(struct.arguments?.[0], '() => Boolean');
174174
});
175+
176+
it('get rid of atomic number operations', async () => {
177+
await getResult({
178+
atomicNumberOperations: false,
179+
schema: `
180+
model User {
181+
id String @id
182+
age Int
183+
rating Float?
184+
}
185+
`,
186+
});
187+
188+
[
189+
'float-field-update-operations.input.ts',
190+
'int-field-update-operations.input.ts',
191+
'string-field-update-operations.input.ts',
192+
].forEach((file) => {
193+
assert(
194+
!sourceFiles.find((s) => s.getFilePath().endsWith(file)),
195+
`File ${file} should not exists`,
196+
);
197+
});
198+
199+
sourceFile = sourceFiles.find((s) => s.getFilePath().endsWith('user-update.input.ts'));
200+
assert(sourceFile);
201+
202+
const classDeclaration = sourceFile.getClass('UserUpdateInput');
203+
assert(classDeclaration);
204+
205+
const id = classDeclaration.getProperty('id')?.getStructure();
206+
assert(id);
207+
assert.equal(id.type, 'string');
208+
let args = classDeclaration
209+
.getProperty('id')
210+
?.getDecorator('Field')
211+
?.getArguments()
212+
.map((a) => a.getText());
213+
assert.equal(args?.[0], '() => String');
214+
215+
const age = classDeclaration.getProperty('age')?.getStructure();
216+
assert(age);
217+
assert.equal(age.type, 'number');
218+
args = classDeclaration
219+
.getProperty('age')
220+
?.getDecorator('Field')
221+
?.getArguments()
222+
.map((a) => a.getText());
223+
assert.equal(args?.[0], '() => Int');
224+
225+
const rating = classDeclaration.getProperty('rating')?.getStructure();
226+
assert(rating);
227+
assert.equal(rating.type, 'number | null');
228+
args = classDeclaration
229+
.getProperty('rating')
230+
?.getDecorator('Field')
231+
?.getArguments()
232+
.map((a) => a.getText());
233+
assert.equal(args?.[0], '() => Float');
234+
});
175235
});

src/generate.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DMMF as PrismaDMMF } from '@prisma/client/runtime/dmmf-types';
22
import { GeneratorOptions } from '@prisma/generator-helper';
33
import assert from 'assert';
4+
import { boolean } from 'boolean';
45
import { existsSync, promises as fs } from 'fs';
56
import { join } from 'path';
67
import { Project, QuoteKind, SourceFile } from 'ts-morph';
@@ -9,7 +10,7 @@ import { generateEnum } from './generate-enum';
910
import { FileType, generateFileName, getFeatureName } from './generate-file-name';
1011
import { generateInput } from './generate-input';
1112
import { generateModel } from './generate-model';
12-
import { mutateScalarInputs } from './mutate-scalar-inputs';
13+
import { mutateFilters } from './mutate-filters';
1314
import { schemaOutputToInput } from './type-utils';
1415

1516
type GenerateArgs = GeneratorOptions & {
@@ -68,7 +69,10 @@ export async function generate(args: GenerateArgs) {
6869
}
6970
// Generate inputs
7071
const inputTypes = prismaClientDmmf.schema.inputTypes.filter(
71-
mutateScalarInputs(prismaClientDmmf.schema.inputTypes),
72+
mutateFilters(prismaClientDmmf.schema.inputTypes, {
73+
combineScalarFilters: boolean(generator.config.combineScalarFilters ?? true),
74+
atomicNumberOperations: boolean(generator.config.atomicNumberOperations ?? false),
75+
}),
7276
);
7377
// Create aggregate inputs
7478
const aggregateInputs = prismaClientDmmf.schema.outputTypes

src/mutate-filters.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { DMMF as PrismaDMMF } from '@prisma/client/runtime/dmmf-types';
2+
3+
type MutateFiltersOptions = {
4+
atomicNumberOperations?: boolean;
5+
combineScalarFilters?: boolean;
6+
};
7+
8+
export function mutateFilters(inputTypes: PrismaDMMF.InputType[], options: MutateFiltersOptions) {
9+
const mutations = [
10+
options.combineScalarFilters && [
11+
combineScalarFilters(inputTypes, [
12+
'StringFilter',
13+
'NullableStringFilter',
14+
'StringNullableFilter',
15+
'NestedStringNullableFilter',
16+
'NestedStringFilter',
17+
]),
18+
combineScalarFilters(inputTypes, [
19+
'BooleanFilter',
20+
'NullableBooleanFilter',
21+
'BoolNullableFilter',
22+
'NestedBoolNullableFilter',
23+
]),
24+
combineScalarFilters(inputTypes, [
25+
'IntFilter',
26+
'NullableIntFilter',
27+
'IntNullableFilter',
28+
'NestedIntNullableFilter',
29+
'NestedIntFilter',
30+
]),
31+
combineScalarFilters(inputTypes, [
32+
'FloatFilter',
33+
'NullableFloatFilter',
34+
'FloatNullableFilter',
35+
'NestedFloatNullableFilter',
36+
'NestedFloatFilter',
37+
]),
38+
combineScalarFilters(inputTypes, [
39+
'DateTimeFilter',
40+
'NullableDateTimeFilter',
41+
'DateTimeNullableFilter',
42+
'NestedDateTimeNullableFilter',
43+
'NestedDateTimeFilter',
44+
]),
45+
combineScalarFilters(inputTypes, [
46+
'StringFieldUpdateOperationsInput',
47+
'NullableStringFieldUpdateOperationsInput',
48+
]),
49+
combineScalarFilters(inputTypes, [
50+
'IntFieldUpdateOperationsInput',
51+
'NullableIntFieldUpdateOperationsInput',
52+
]),
53+
combineScalarFilters(inputTypes, [
54+
'FloatFieldUpdateOperationsInput',
55+
'NullableFloatFieldUpdateOperationsInput',
56+
]),
57+
combineScalarFilters(inputTypes, [
58+
'BoolFieldUpdateOperationsInput',
59+
'NullableBoolFieldUpdateOperationsInput',
60+
]),
61+
combineScalarFilters(inputTypes, [
62+
'DateTimeFieldUpdateOperationsInput',
63+
'NullableDateTimeFieldUpdateOperationsInput',
64+
]),
65+
],
66+
!options.atomicNumberOperations &&
67+
noAtomicNumberOperations([
68+
'StringFieldUpdateOperationsInput',
69+
'NullableStringFieldUpdateOperationsInput',
70+
'IntFieldUpdateOperationsInput',
71+
'NullableIntFieldUpdateOperationsInput',
72+
'FloatFieldUpdateOperationsInput',
73+
'NullableFloatFieldUpdateOperationsInput',
74+
'BoolFieldUpdateOperationsInput',
75+
'NullableBoolFieldUpdateOperationsInput',
76+
'DateTimeFieldUpdateOperationsInput',
77+
'NullableDateTimeFieldUpdateOperationsInput',
78+
]),
79+
];
80+
81+
return function (inputType: PrismaDMMF.InputType) {
82+
for (const mutation of mutations.filter(Boolean).flat()) {
83+
const result = mutation && mutation(inputType);
84+
if (!result) {
85+
return false;
86+
}
87+
inputType = result;
88+
}
89+
return inputType;
90+
};
91+
}
92+
93+
function combineScalarFilters(inputTypes: PrismaDMMF.InputType[], names: string[]) {
94+
let counter = 0;
95+
let type: PrismaDMMF.InputType | undefined;
96+
const [main] = names;
97+
for (const name of names) {
98+
type = inputTypes.find((t) => t.name === name);
99+
if (type) {
100+
type = mutateFieldsType(type, names);
101+
break;
102+
}
103+
}
104+
105+
return (inputType: PrismaDMMF.InputType) => {
106+
if (!type) {
107+
return inputType;
108+
}
109+
if (names.includes(inputType.name)) {
110+
counter++;
111+
if (counter > 1) {
112+
return false;
113+
}
114+
inputType.name = main;
115+
inputType.fields = type.fields;
116+
return inputType;
117+
}
118+
inputType = mutateFieldsType(inputType, names);
119+
return inputType;
120+
};
121+
122+
function mutateFieldsType(inputType: PrismaDMMF.InputType, names: string[]) {
123+
const [main] = names;
124+
inputType.fields.forEach((field) => {
125+
field.inputType.forEach((input) => {
126+
if (names.includes(String(input.type))) {
127+
input.type = main;
128+
}
129+
});
130+
});
131+
return inputType;
132+
}
133+
}
134+
135+
function noAtomicNumberOperations(names: string[]) {
136+
return (inputType: PrismaDMMF.InputType) => {
137+
if (names.includes(inputType.name)) {
138+
return false;
139+
}
140+
inputType.fields = inputType.fields.map((field) => {
141+
field.inputType = field.inputType.filter((inputType) => {
142+
return !names.includes(String(inputType.type));
143+
});
144+
return field;
145+
});
146+
return inputType;
147+
};
148+
}

0 commit comments

Comments
 (0)