Created
May 17, 2021 03:04
-
-
Save deckchairlabs/8a11c33311c01273deec7e739417dbc9 to your computer and use it in GitHub Desktop.
Prisma Custom Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
const path = require('path') | |
const generatorHelper = require('@prisma/generator-helper') | |
const { Project, StructureKind, VariableDeclarationKind } = require('ts-morph') | |
generatorHelper.generatorHandler({ | |
onManifest(config) { | |
return { | |
prettyName: 'Filters', | |
defaultOutput: path.resolve(__dirname, 'filters'), | |
requiresGenerators: ['nexus-prisma'], | |
} | |
}, | |
onGenerate(options) { | |
const project = new Project({ | |
skipAddingFilesFromTsConfig: true, | |
}) | |
const outputPath = options.generator.output.value | |
const enums = options.dmmf.datamodel.enums | |
const models = options.dmmf.datamodel.models | |
const indexSource = project.createSourceFile( | |
`${outputPath}/index.ts`, | |
{ | |
statements: [ | |
{ | |
kind: StructureKind.ImportDeclaration, | |
namedImports: ['Prisma'], | |
moduleSpecifier: '@prisma/client', | |
}, | |
{ | |
kind: StructureKind.ImportDeclaration, | |
namedImports: ['enumType', 'inputObjectType'], | |
moduleSpecifier: 'nexus', | |
}, | |
], | |
}, | |
{ | |
overwrite: true, | |
} | |
) | |
if (models.length > 0) { | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'SortOrderEnum', | |
initializer(writer) { | |
writer | |
.write('enumType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'SortOrder',`) | |
writer.writeLine('members: Prisma.SortOrder') | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'IDFilterInputType', | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'IDFilterInput',`) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine(`t.id('equals')`) | |
writer.writeLine(`t.list.id('in')`) | |
writer.writeLine(`t.list.id('notIn')`) | |
writer.writeLine(`t.id('lt')`) | |
writer.writeLine(`t.id('lte')`) | |
writer.writeLine(`t.id('gt')`) | |
writer.writeLine(`t.id('gte')`) | |
writer.writeLine( | |
`t.field('not', { type: 'IDFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'StringFilterInputType', | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'StringFilterInput',`) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine(`t.string('equals')`) | |
writer.writeLine(`t.string('contains')`) | |
writer.writeLine(`t.string('startsWith')`) | |
writer.writeLine(`t.string('endsWith')`) | |
writer.writeLine(`t.list.string('in')`) | |
writer.writeLine(`t.list.string('notIn')`) | |
writer.writeLine(`t.string('lt')`) | |
writer.writeLine(`t.string('lte')`) | |
writer.writeLine(`t.string('gt')`) | |
writer.writeLine(`t.string('gte')`) | |
writer.writeLine( | |
`t.field('not', { type: 'StringFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'DateTimeFilterInputType', | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'DateTimeFilterInput',`) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine(`t.field('equals', { type: 'DateTime' })`) | |
writer.writeLine(`t.list.field('in', { type: 'DateTime' })`) | |
writer.writeLine( | |
`t.list.field('notIn', { type: 'DateTime' })` | |
) | |
writer.writeLine(`t.field('lt', { type: 'DateTime' })`) | |
writer.writeLine(`t.field('lte', { type: 'DateTime' })`) | |
writer.writeLine(`t.field('gt', { type: 'DateTime' })`) | |
writer.writeLine(`t.field('gte', { type: 'DateTime' })`) | |
writer.writeLine( | |
`t.field('not', { type: 'DateTimeFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'IntFilterInputType', | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'IntFilterInput',`) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine(`t.int('equals')`) | |
writer.writeLine(`t.list.int('in')`) | |
writer.writeLine(`t.list.int('notIn')`) | |
writer.writeLine(`t.int('lt')`) | |
writer.writeLine(`t.int('lte')`) | |
writer.writeLine(`t.int('gt')`) | |
writer.writeLine(`t.int('gte')`) | |
writer.writeLine( | |
`t.field('not', { type: 'IntFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: 'BooleanFilterInputType', | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine(`name: 'BooleanFilterInput',`) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine(`t.boolean('equals')`) | |
writer.writeLine( | |
`t.field('not', { type: 'BooleanFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
models.forEach((model) => { | |
indexSource.addExportDeclaration({ | |
moduleSpecifier: `./${model.name}`, | |
namespaceExport: model.name, | |
}) | |
const whereUniqueInputTypeName = `${model.name}WhereUniqueInput` | |
const whereInputTypeName = `${model.name}WhereInput` | |
const orderByInputTypeName = `${model.name}OrderByInput` | |
const modelRelationFilterName = `${model.name}RelationFilterInput` | |
const modelListRelationFilterName = `${model.name}ListRelationFilterInput` | |
const sourceFile = project.createSourceFile( | |
`${outputPath}/${model.name}.ts`, | |
{ | |
statements: [ | |
{ | |
kind: StructureKind.ImportDeclaration, | |
namedImports: ['inputObjectType'], | |
moduleSpecifier: 'nexus', | |
}, | |
], | |
}, | |
{ | |
overwrite: true, | |
} | |
) | |
sourceFile.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${whereUniqueInputTypeName}Type`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.inlineBlock(() => { | |
writer.writeLine(`name: '${whereUniqueInputTypeName}',`) | |
writer.writeLine('definition(t)') | |
writer.inlineBlock(() => { | |
writer.writeLine(`t.nonNull.id('id')`) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
sourceFile.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${whereInputTypeName}Type`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.inlineBlock(() => { | |
writer.writeLine(`name: '${whereInputTypeName}',`) | |
writer.writeLine('definition(t)') | |
writer.inlineBlock(() => { | |
model.fields.forEach((field) => { | |
if (field.kind === 'scalar') { | |
if ( | |
field.name === 'id' || | |
field.name.endsWith('Id') | |
) { | |
writer.write(`t.field('${field.name}',`) | |
writer | |
.inlineBlock(() => { | |
writer.writeLine(`type: 'IDFilterInput'`) | |
}) | |
.write(')') | |
.newLine() | |
} else { | |
writer.write(`t.field('${field.name}',`) | |
writer | |
.inlineBlock(() => { | |
writer.writeLine( | |
`type: '${field.type}FilterInput'` | |
) | |
}) | |
.write(')') | |
.newLine() | |
} | |
} else if (field.kind === 'object') { | |
if (field.isList) { | |
writer | |
.write(`t.field('${field.name}',`) | |
.inlineBlock(() => { | |
writer.writeLine( | |
`type: '${field.type}ListRelationFilterInput'` | |
) | |
}) | |
.write(')') | |
.newLine() | |
} else { | |
writer | |
.write(`t.field('${field.name}',`) | |
.inlineBlock(() => { | |
writer.writeLine( | |
`type: '${field.type}RelationFilterInput'` | |
) | |
}) | |
.write(')') | |
.newLine() | |
} | |
} else if (field.kind === 'enum') { | |
writer | |
.write(`t.field('${field.name}',`) | |
.inlineBlock(() => { | |
writer.writeLine( | |
`type: '${field.type}EnumFilterInput'` | |
) | |
}) | |
.write(')') | |
.newLine() | |
} | |
}) | |
writer.writeLine( | |
`t.list.field('AND', { type: '${whereInputTypeName}' })` | |
) | |
writer.writeLine( | |
`t.list.field('OR', { type: '${whereInputTypeName}' })` | |
) | |
writer.writeLine( | |
`t.list.field('NOT', { type: '${whereInputTypeName}' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
sourceFile.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${modelRelationFilterName}Type`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.inlineBlock(() => { | |
writer.writeLine(`name: '${modelRelationFilterName}',`) | |
writer.writeLine('definition(t)') | |
writer.inlineBlock(() => { | |
writer.writeLine( | |
`t.field('is', { type: '${whereInputTypeName}' })` | |
) | |
writer.writeLine( | |
`t.field('isNot', { type: '${whereInputTypeName}' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
sourceFile.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${modelListRelationFilterName}Type`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.inlineBlock(() => { | |
writer.writeLine(`name: '${modelListRelationFilterName}',`) | |
writer.writeLine('definition(t)') | |
writer.inlineBlock(() => { | |
writer.writeLine( | |
`t.field('every', { type: '${whereInputTypeName}' })` | |
) | |
writer.writeLine( | |
`t.field('some', { type: '${whereInputTypeName}' })` | |
) | |
writer.writeLine( | |
`t.field('none', { type: '${whereInputTypeName}' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
sourceFile.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${orderByInputTypeName}Type`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.inlineBlock(() => { | |
writer.writeLine(`name: '${orderByInputTypeName}',`) | |
writer.writeLine('definition(t)') | |
writer.inlineBlock(() => { | |
model.fields.forEach((field) => { | |
if (field.kind === 'scalar' || field.kind === 'enum') { | |
writer.writeLine( | |
`t.field('${field.name}', { type: 'SortOrder' })` | |
) | |
} else if (field.kind === 'object') { | |
const relatedModel = models.find( | |
(model) => model.name === field.type | |
) | |
if (relatedModel) { | |
writer.writeLine( | |
`t.field('${field.name}', { type: '${relatedModel.name}OrderByInput' })` | |
) | |
} | |
} | |
}) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
sourceFile.formatText({ | |
indentSize: 2, | |
convertTabsToSpaces: true, | |
semicolons: 'remove', | |
}) | |
}) | |
if (enums.length > 0) { | |
enums.forEach((enumModel) => { | |
indexSource.addVariableStatement({ | |
declarationKind: VariableDeclarationKind.Const, | |
isExported: true, | |
declarations: [ | |
{ | |
name: `${enumModel.name}EnumFilterInputType`, | |
initializer(writer) { | |
writer | |
.write('inputObjectType(') | |
.indent(1) | |
.inlineBlock(() => { | |
writer.writeLine( | |
`name: '${enumModel.name}EnumFilterInput',` | |
) | |
writer.write('definition(t)') | |
writer.block(() => { | |
writer.writeLine( | |
`t.field('equals', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.list.field('in', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.list.field('notIn', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.field('lt', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.field('lte', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.field('gt', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.field('gte', { type: '${enumModel.name}' })` | |
) | |
writer.writeLine( | |
`t.field('not', { type: '${enumModel.name}EnumFilterInput' })` | |
) | |
}) | |
}) | |
.write(')') | |
.newLine() | |
}, | |
}, | |
], | |
}) | |
}) | |
} | |
indexSource.formatText({ | |
indentSize: 2, | |
convertTabsToSpaces: true, | |
semicolons: 'remove', | |
}) | |
} | |
project.save() | |
}, | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
generator prisma_client { | |
provider = "prisma-client-js" | |
previewFeatures = ["orderByRelation"] | |
} | |
// Add your custom generator | |
generator filters { | |
provider = "./prisma/filters.js" | |
output = "./api/src/generated/filters" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for this nice gist!
In my case to make it work, I needed to make
onGenerate
an async function, and wait forproject.save()
to finish, otherwise I could not find any generated files in the folder.Line 14:
async onGenerate(options) {
Line 535:
await project.save()