Skip to content

Commit

Permalink
feat: @PropertyType() to replace types_ configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
unlight committed Apr 25, 2021
1 parent 4ce28f1 commit 4a7313d
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 57 deletions.
63 changes: 59 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ generator nestgraphql {
fields_{Namespace}_output = true | false
fields_{Namespace}_defaultImport = "default import name" | true
fields_{Namespace}_namespaceImport = "namespace import name"
fields_{Namespace}_namedImport = true | false
}
```

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

##### `fields_{Namespace}_from`

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

##### `fields_{Namespace}_input`

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

Example:
##### `fields_{Namespace}_namedImport`

If imported module has internal namespace, this allow to generate named import,
imported name will be equal to `{Namespace}`, see [example of usage](#propertytype)
Type: `boolean`
Default: `false`

Custom decorators example:

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

##### @PropertyType()

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

Named import example:

```prisma
model Transfer {
id String @id
/// @PropertyType({ name: 'Prisma.Decimal', from: '@prisma/client', namedImport: true })
money Decimal
}
```

May generate following:

```ts
import { Prisma } from '@prisma/client';

@ObjectType()
export class User {
@Field(() => GraphQLDecimal)
money!: Prisma.Decimal;
}
```

Another example:

```
model User {
id String @id
/// @PropertyType('TF.JsonObject')
data Json
}
```

May generate:

```ts
import * as TF from 'type-fest';

@ObjectType()
export class User {
@Field(() => GraphQLJSON)
data!: TF.JsonObject;
}
```

## Similar Projects

- https://github.com/wSedlacek/prisma-generators/tree/master/libs/nestjs
Expand Down
3 changes: 3 additions & 0 deletions src/@generated/dummy/dummy-max-order-by-aggregate.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export class DummyMaxOrderByAggregateInput {
@Field(() => SortOrder, { nullable: true })
id?: SortOrder;

@Field(() => SortOrder, { nullable: true })
created?: SortOrder;

@Field(() => SortOrder, { nullable: true })
floaty?: SortOrder;

Expand Down
21 changes: 16 additions & 5 deletions src/handlers/input-type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from 'assert';
import JSON5 from 'json5';
import { castArray, trim } from 'lodash';
import { ClassDeclarationStructure, StructureKind } from 'ts-morph';

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

const propertyType = getPropertyType({
location,
type: typeName,
});
const propertyType = castArray(
propertySettings?.name ||
customType?.fieldType?.split('|').map(trim) ||
getPropertyType({
location,
type: typeName,
}),
);

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

classStructure.properties?.push(property);

if (propertySettings) {
importDeclarations.create({ ...propertySettings });
}

let graphqlType: string;
const fieldType = settings?.getFieldType();

Expand Down Expand Up @@ -141,7 +152,7 @@ export function inputType(
});

for (const options of settings || []) {
if (!options.input || options.isFieldType) {
if (!options.input || options.kind !== 'Decorator') {
continue;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand Down
34 changes: 24 additions & 10 deletions src/handlers/model-output-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'assert';
import JSON5 from 'json5';
import { remove } from 'lodash';
import { castArray, remove, trim } from 'lodash';
import {
ClassDeclarationStructure,
CommentStatement,
Expand Down Expand Up @@ -99,16 +99,27 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
fileType = 'output';
outputTypeName = getOutputTypeName(outputTypeName);
}
const customType = config.types[outputTypeName];
const customType = config.types[outputTypeName]; // todo: remove
const modelField = modelFields.get(model.name)?.get(field.name);
const settings = fieldSettings.get(model.name)?.get(field.name);
const fieldType = settings?.getFieldType();
const propertySettings = settings?.getPropertyType();

const propertyType = castArray(
propertySettings?.name ||
customType?.fieldType?.split('|').map(trim) ||
getPropertyType({
location,
type: outputTypeName,
}),
);

// For model we keep only one type
propertyType.splice(1, propertyType.length);

const propertyType = customType?.fieldType
? [customType.fieldType]
: getPropertyType({
location,
type: outputTypeName,
});
if (field.isNullable && !isList && ['enumTypes', 'scalar'].includes(location)) {
propertyType.push('null');
}

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

let graphqlType: string;
const fieldType = settings?.getFieldType();

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

classStructure.properties?.push(property);

if (propertySettings) {
importDeclarations.create({ ...propertySettings });
}

// Create import for typescript field/property type
if (customType && customType.fieldType && customType.fieldModule) {
importDeclarations.add(customType.fieldType, customType.fieldModule);
Expand All @@ -190,7 +204,7 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
});

for (const options of settings || []) {
if (!options.output || options.isFieldType) {
if (!options.output || options.kind !== 'Decorator') {
continue;
}
property.decorators?.push({
Expand Down
37 changes: 31 additions & 6 deletions src/handlers/output-type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import assert from 'assert';
import JSON5 from 'json5';
import { castArray, trim } from 'lodash';
import { ClassDeclarationStructure, StructureKind } from 'ts-morph';

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

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

field.outputType.type = outputTypeName;

const propertyType = customType?.fieldType
? [customType.fieldType]
: getPropertyType({
location,
type: outputTypeName,
});
const propertyType = castArray(
propertySettings?.name ||
customType?.fieldType?.split('|').map(trim) ||
getPropertyType({
location,
type: outputTypeName,
}),
);

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

classStructure.properties?.push(property);

if (propertySettings) {
importDeclarations.create({ ...propertySettings });
}

const graphqlImport = getGraphqlImport({
sourceFile,
fileType,
Expand Down Expand Up @@ -123,6 +133,21 @@ export function outputType(outputType: OutputType, args: EventArguments) {
}),
],
});

for (const options of settings || []) {
if (!options.output || options.kind !== 'Decorator') {
continue;
}
property.decorators?.push({
name: options.name,
arguments: options.arguments,
});
assert(
options.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(options);
}
}

eventEmitter.emitSync('ClassProperty', property, { location, isList });
Expand Down
11 changes: 0 additions & 11 deletions src/helpers/create-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@ describe('createConfig', () => {
expect(result.reExport).toEqual(ReExport.None);
});

it('createConfig types', () => {
const result = createConfig({
types_Decimal_fieldType: `MyDec`,
types_Decimal_fieldModule: `decimal.js`,
});
expect(result.types['Decimal']).toBeTruthy();
expect(result.types['Decimal']?.fieldType).toEqual('MyDec');
expect(result.types['Decimal']?.fieldModule).toEqual('decimal.js');
expect(result.$warnings).toEqual([]);
});

it('filename with parent reference should be not valid', () => {
const result = createConfig({
outputFilePattern: '../../../{model}//{name}.{type}.ts/',
Expand Down
6 changes: 6 additions & 0 deletions src/helpers/create-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export function createConfig(data: Record<string, string | undefined>) {
}),
);

if (Object.keys(types).length > 0) {
$warnings.push(
'Configuration throu `types_*` is deprecated, use @FieldType/@PropertyType https://github.com/unlight/prisma-nestjs-graphql#field-settings',
);
}

return {
outputFilePattern,
tsConfigFilePath: 'tsconfig.json' as string,
Expand Down
Loading

0 comments on commit 4a7313d

Please sign in to comment.