Skip to content

Commit a9b0e46

Browse files
authored
feat(match): Allows match expressions in FieldType and PropertyType (unlight#60)
1 parent f080490 commit a9b0e46

File tree

6 files changed

+174
-21
lines changed

6 files changed

+174
-21
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,10 @@ export class UserCreateInput {
422422

423423
#### @FieldType()
424424

425-
Allow set custom type for field
425+
Allow set custom GraphQL scalar type for field
426+
427+
To override scalar type in specific classes, you can use glob pattern `match: string | string[]`
428+
see [outmatch](https://github.com/axtgr/outmatch#usage) for details.
426429

427430
```prisma
428431
model User {
@@ -479,6 +482,9 @@ Missing field options will merged from generator configuration.
479482

480483
Similar to `@FieldType()` but refer to TypeScript property (actually field too).
481484

485+
To override TypeScript type in specific classes, you can use glob pattern `match: string | string[]`
486+
see [outmatch](https://github.com/axtgr/outmatch#usage) for details.
487+
482488
Named import example:
483489

484490
```prisma

src/handlers/input-type.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ export function inputType(
8282
const { isList, location, type } = graphqlInputType;
8383
const typeName = String(type);
8484
const settings = modelFieldSettings?.get(name);
85-
const propertySettings = settings?.getPropertyType();
85+
const propertySettings = settings?.getPropertyType({
86+
name: inputType.name,
87+
input: true,
88+
});
8689
const isCustomsApplicable =
8790
typeName === model?.fields.find(f => f.name === name)?.type;
8891
const propertyType = castArray(
@@ -98,7 +101,6 @@ export function inputType(
98101
propertyType,
99102
isList,
100103
});
101-
102104
classStructure.properties?.push(property);
103105

104106
if (propertySettings) {
@@ -112,9 +114,12 @@ export function inputType(
112114
ok(property.decorators);
113115

114116
let graphqlType: string;
115-
const fieldType = settings?.getFieldType();
117+
const fieldType = settings?.getFieldType({
118+
name: inputType.name,
119+
input: true,
120+
});
116121

117-
if (fieldType && fieldType.input && isCustomsApplicable) {
122+
if (fieldType && isCustomsApplicable) {
118123
graphqlType = fieldType.name;
119124
importDeclarations.create({ ...fieldType });
120125
} else {

src/handlers/model-output-type.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,14 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
8282
}
8383
const modelField = modelFields.get(model.name)?.get(field.name);
8484
const settings = fieldSettings.get(model.name)?.get(field.name);
85-
const fieldType = settings?.getFieldType();
86-
const propertySettings = settings?.getPropertyType();
85+
const fieldType = settings?.getFieldType({
86+
name: outputType.name,
87+
output: true,
88+
});
89+
const propertySettings = settings?.getPropertyType({
90+
name: outputType.name,
91+
output: true,
92+
});
8793

8894
const propertyType = castArray(
8995
propertySettings?.name ||
@@ -102,7 +108,7 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
102108

103109
let graphqlType: string;
104110

105-
if (fieldType && fieldType.output) {
111+
if (fieldType) {
106112
graphqlType = fieldType.name;
107113
importDeclarations.create({ ...fieldType });
108114
} else {

src/handlers/output-type.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export function outputType(outputType: OutputType, args: EventArguments) {
5959
const settings = isCountOutput
6060
? undefined
6161
: model && fieldSettings.get(model.name)?.get(field.name);
62-
const propertySettings = settings?.getPropertyType();
62+
const propertySettings = settings?.getPropertyType({
63+
name: outputType.name,
64+
output: true,
65+
});
6366
const isCustomsApplicable =
6467
outputTypeName === model?.fields.find(f => f.name === field.name)?.type;
6568

@@ -93,9 +96,12 @@ export function outputType(outputType: OutputType, args: EventArguments) {
9396
property.decorators.push({ name: 'HideField', arguments: [] });
9497
} else {
9598
let graphqlType: string;
96-
const fieldType = settings?.getFieldType();
99+
const fieldType = settings?.getFieldType({
100+
name: outputType.name,
101+
output: true,
102+
});
97103

98-
if (fieldType && fieldType.output && isCustomsApplicable) {
104+
if (fieldType && isCustomsApplicable) {
99105
graphqlType = fieldType.name;
100106
importDeclarations.create({ ...fieldType });
101107
} else {

src/helpers/object-settings.ts

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,18 @@ export type ObjectSetting = {
2222
namedImport?: boolean;
2323
};
2424

25+
interface ObjectSettingsFilterArgs {
26+
name: string;
27+
input?: boolean;
28+
output?: boolean;
29+
}
30+
2531
export class ObjectSettings extends Array<ObjectSetting> {
2632
shouldHideField({
2733
name,
2834
input = false,
2935
output = false,
30-
}: {
31-
name: string;
32-
input?: boolean;
33-
output?: boolean;
34-
}): boolean {
36+
}: ObjectSettingsFilterArgs): boolean {
3537
const hideField = this.find(s => s.name === 'HideField');
3638

3739
return Boolean(
@@ -41,13 +43,51 @@ export class ObjectSettings extends Array<ObjectSetting> {
4143
);
4244
}
4345

44-
getFieldType() {
45-
return this.find(s => s.kind === 'FieldType');
46+
/* eslint-disable consistent-return */
47+
getFieldType({
48+
name,
49+
input,
50+
output,
51+
}: ObjectSettingsFilterArgs): ObjectSetting | undefined {
52+
const fieldType = this.find(s => s.kind === 'FieldType');
53+
54+
if (!fieldType) {
55+
return undefined;
56+
}
57+
58+
if (fieldType.match) {
59+
// eslint-disable-next-line unicorn/prefer-regexp-test
60+
return fieldType.match(name) ? fieldType : undefined;
61+
}
62+
63+
if (input && !fieldType.input) {
64+
return undefined;
65+
}
66+
67+
if (output && !fieldType.output) {
68+
return undefined;
69+
}
70+
71+
return fieldType;
4672
}
73+
/* eslint-enable consistent-return */
74+
75+
/* eslint-disable consistent-return */
76+
getPropertyType({ name }: ObjectSettingsFilterArgs): ObjectSetting | undefined {
77+
const propertyType = this.find(s => s.kind === 'PropertyType');
78+
79+
if (!propertyType) {
80+
return undefined;
81+
}
4782

48-
getPropertyType() {
49-
return this.find(s => s.kind === 'PropertyType');
83+
// eslint-disable-next-line unicorn/prefer-regexp-test
84+
if (propertyType.match && !propertyType.match(name)) {
85+
return undefined;
86+
}
87+
88+
return propertyType;
5089
}
90+
/* eslint-enable consistent-return */
5191

5292
getObjectTypeArguments(options: Record<string, any>): string[] {
5393
const objectTypeOptions = merge({}, options);
@@ -146,6 +186,11 @@ function customType(args: string) {
146186
if ((options as { name: string | undefined }).name?.includes('.')) {
147187
result.namespaceImport = namespace;
148188
}
189+
190+
if (typeof options.match === 'string' || Array.isArray(options.match)) {
191+
result.match = outmatch(options.match, { separator: false });
192+
}
193+
149194
return result;
150195
}
151196

src/test/generate.spec.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ describe('model with one id int', () => {
122122

123123
it('object type description', () => {
124124
const decoratorArgument = classFile.getDecorators()[0].getStructure()
125-
?.arguments?.[0] as string | undefined;
125+
.arguments?.[0] as string | undefined;
126126
expect(decoratorArgument).toMatch(/description:\s*["']User really["']/);
127127
});
128128

@@ -2081,6 +2081,48 @@ describe('compound index', () => {
20812081
});
20822082
});
20832083

2084+
describe('field type', () => {
2085+
describe('it overwrites field type based on match expression', () => {
2086+
before(async () => {
2087+
({ project, sourceFiles } = await testGenerate({
2088+
schema: `
2089+
model User {
2090+
id Int @id
2091+
/// @FieldType({ name: 'GraphQLJSONObject', from: 'graphql-scalars', namedImport: true, match: 'User{Create,Update}Input' })
2092+
profile Json
2093+
}
2094+
`,
2095+
options: [`outputFilePattern = "{name}.{type}.ts"`],
2096+
}));
2097+
});
2098+
2099+
it('should use default scalar type in model', () => {
2100+
setSourceFile('user.model.ts');
2101+
expect(t('profile')).toEqual('() => GraphQLJSON');
2102+
});
2103+
2104+
it('should use default scalar type in user-create-many.input', () => {
2105+
setSourceFile('user-create-many.input.ts');
2106+
expect(t('profile')).toEqual('() => GraphQLJSON');
2107+
});
2108+
2109+
it('user-create.input', () => {
2110+
setSourceFile('user-create.input.ts');
2111+
expect(t('profile')).toEqual('() => GraphQLJSONObject');
2112+
});
2113+
2114+
it('should use default scalar type in user-update-many-mutation.input', () => {
2115+
setSourceFile('user-update-many-mutation.input.ts');
2116+
expect(t('profile')).toEqual('() => GraphQLJSON');
2117+
});
2118+
2119+
it('user-update.input', () => {
2120+
setSourceFile('user-update.input.ts');
2121+
expect(t('profile')).toEqual('() => GraphQLJSONObject');
2122+
});
2123+
});
2124+
});
2125+
20842126
it('fieldtype on groupby', async () => {
20852127
({ project, sourceFiles } = await testGenerate({
20862128
schema: `
@@ -2097,6 +2139,49 @@ it('fieldtype on groupby', async () => {
20972139
expect(t('profile')).toEqual('() => GraphQLJSONObject');
20982140
});
20992141

2142+
describe('property type', () => {
2143+
describe('it overwrites property type based on match expression', () => {
2144+
before(async () => {
2145+
({ project, sourceFiles } = await testGenerate({
2146+
schema: `
2147+
model User {
2148+
id Int @id
2149+
/// @PropertyType({ name: 'JsonObject', from: 'type-fest', namedImport: true, match: 'User{Create,Update}Input' })
2150+
profile Json
2151+
}
2152+
`,
2153+
options: [`outputFilePattern = "{name}.{type}.ts"`],
2154+
}));
2155+
});
2156+
2157+
it('should use default scalar type in model', () => {
2158+
setSourceFile('user.model.ts');
2159+
expect(p('profile')?.type).toEqual('any');
2160+
});
2161+
2162+
it('should use default scalar type in user-create-many.input', () => {
2163+
setSourceFile('user-create-many.input.ts');
2164+
expect(p('profile')?.type).toEqual('any');
2165+
});
2166+
2167+
it('user-create.input', () => {
2168+
setSourceFile('user-create.input.ts');
2169+
expect(p('profile')?.type).toEqual('JsonObject');
2170+
2171+
});
2172+
2173+
it('should use default scalar type in user-update-many-mutation.input', () => {
2174+
setSourceFile('user-update-many-mutation.input.ts');
2175+
expect(p('profile')?.type).toEqual('any');
2176+
});
2177+
2178+
it('user-update.input', () => {
2179+
setSourceFile('user-update.input.ts');
2180+
expect(p('profile')?.type).toEqual('JsonObject');
2181+
});
2182+
});
2183+
});
2184+
21002185
it('hidefield on groupby', async () => {
21012186
({ project, sourceFiles } = await testGenerate({
21022187
schema: `

0 commit comments

Comments
 (0)