@baeta/plugin-prisma 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,86 @@
1
+ import { filterEmpty } from '../../utils/string';
2
+ import { DMMF } from '../dmmf';
3
+ import { ModelsOperationsMap } from '../model-operations';
4
+ import { printObject } from './object';
5
+ import { mapPrismaTypeToScalar } from './prisma-scalar';
6
+ import { printType } from './type';
7
+
8
+ export function printPrismaOutput(outputType: DMMF.OutputType) {
9
+ const content = outputType.fields.map((field) => printOutputTypeField(field));
10
+ return printObject('type', outputType.name, content);
11
+ }
12
+
13
+ export function printPrismaOperation(
14
+ operations: ModelsOperationsMap,
15
+ field: DMMF.SchemaField
16
+ ) {
17
+ const operation = operations[field.name];
18
+
19
+ if (!operation) {
20
+ return;
21
+ }
22
+
23
+ const args = printOutputFieldArgs(field.args);
24
+ const output = printOutputFieldReturnType(field);
25
+ return `${operation.operation}${args}: ${output}`;
26
+ }
27
+
28
+ function printOutputTypeField(field: DMMF.SchemaField) {
29
+ const args = printOutputFieldArgs(field.args);
30
+ const returnType = printOutputFieldReturnType(field);
31
+ return `${field.name}${args}: ${returnType}`;
32
+ }
33
+
34
+ function printOutputFieldArgs(args: DMMF.SchemaArg[]) {
35
+ const argList = filterEmpty(args.map((arg) => printOutputFieldArg(arg)));
36
+
37
+ if (argList.length === 0) {
38
+ return '';
39
+ }
40
+
41
+ return `(${argList.join(', ')})`;
42
+ }
43
+
44
+ function printOutputFieldArg(arg: DMMF.SchemaArg) {
45
+ const type = printOutputFieldArgType(arg);
46
+
47
+ if (!type) {
48
+ return;
49
+ }
50
+
51
+ return `${arg.name}: ${type}`;
52
+ }
53
+
54
+ function printOutputFieldArgType(arg: DMMF.SchemaArg) {
55
+ const inputType = arg.inputTypes[0];
56
+
57
+ if (!inputType) {
58
+ return;
59
+ }
60
+
61
+ let type = inputType.type.toString();
62
+
63
+ if (inputType.location === 'scalar') {
64
+ type = mapPrismaTypeToScalar(type);
65
+ }
66
+
67
+ if (inputType.isList) {
68
+ type = `[${type}!]`;
69
+ }
70
+
71
+ if (arg.isRequired) {
72
+ type += '!';
73
+ }
74
+
75
+ return type;
76
+ }
77
+
78
+ function printOutputFieldReturnType(field: DMMF.SchemaField) {
79
+ const outputType = field.outputType;
80
+
81
+ return printType(outputType.type.toString(), {
82
+ kind: outputType.location,
83
+ list: outputType.isList,
84
+ required: !field.isNullable,
85
+ });
86
+ }
@@ -0,0 +1,46 @@
1
+ export enum PrismaScalars {
2
+ string = 'String',
3
+ boolean = 'Boolean',
4
+ int = 'Int',
5
+ float = 'Float',
6
+ dateTime = 'DateTime',
7
+ json = 'Json',
8
+ bigInt = 'BigInt',
9
+ decimal = 'Decimal',
10
+ bytes = 'Bytes',
11
+ }
12
+
13
+ export function mapPrismaTypeToScalar(scalar: string) {
14
+ switch (scalar) {
15
+ case PrismaScalars.string: {
16
+ return 'String';
17
+ }
18
+ case PrismaScalars.boolean: {
19
+ return 'Boolean';
20
+ }
21
+ case PrismaScalars.int: {
22
+ return 'Int';
23
+ }
24
+ case PrismaScalars.float: {
25
+ return 'Float';
26
+ }
27
+ case PrismaScalars.dateTime: {
28
+ return 'DateTime';
29
+ }
30
+ case PrismaScalars.json: {
31
+ return 'Json';
32
+ }
33
+ case PrismaScalars.bigInt: {
34
+ return 'BigInt';
35
+ }
36
+ case PrismaScalars.decimal: {
37
+ return 'Decimal';
38
+ }
39
+ case PrismaScalars.bytes: {
40
+ return 'Bytes';
41
+ }
42
+ default: {
43
+ throw new Error(`Unrecognized scalar type: ${scalar}`);
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,37 @@
1
+ import { mapPrismaTypeToScalar } from './prisma-scalar';
2
+
3
+ interface ScalarOptions {
4
+ kind?: string;
5
+ list?: boolean;
6
+ required?: boolean;
7
+ }
8
+
9
+ export function printType(name: string, options: ScalarOptions = {}) {
10
+ let type = name;
11
+
12
+ if (options.kind === 'scalar' && name !== 'ID') {
13
+ type = mapPrismaTypeToScalar(type);
14
+ }
15
+
16
+ if (options.list) {
17
+ type = `[${type}!]`;
18
+ }
19
+
20
+ if (options.required) {
21
+ type += '!';
22
+ }
23
+
24
+ return type;
25
+ }
26
+
27
+ export const scalars = [
28
+ 'scalar DateTime',
29
+ 'scalar BigInt',
30
+ 'scalar Json',
31
+ 'scalar Decimal',
32
+ 'scalar Bytes',
33
+ ];
34
+
35
+ export function printScalars() {
36
+ return scalars.join('\n');
37
+ }
@@ -0,0 +1 @@
1
+ export * from './resolver-builder';
@@ -0,0 +1,69 @@
1
+ import { File } from '@baeta/plugin';
2
+ import { join } from 'path';
3
+ import { Casing, changeCase } from '../../utils/casing';
4
+ import { printModule, printModuleExport } from '../print/module';
5
+ import { printPrismaModule } from '../print/module-prisma';
6
+ import { ModuleResolvers, ModuleResolversMap } from './resolver-builder';
7
+
8
+ const tag = 'prisma-resolver';
9
+
10
+ export interface ModuleFileOptions {
11
+ root: string;
12
+ casing: Casing;
13
+ namespace: string;
14
+ filename: string;
15
+ createExport?: boolean;
16
+ }
17
+
18
+ export function createModuleFiles(map: ModuleResolversMap, options: ModuleFileOptions) {
19
+ const files: File[] = [];
20
+
21
+ for (const model of Object.values(map)) {
22
+ if (!model) {
23
+ continue;
24
+ }
25
+
26
+ const modulePath = createModulePath(
27
+ model,
28
+ options.root,
29
+ options.namespace,
30
+ options.casing
31
+ );
32
+
33
+ files.push(createModuleResolvers(model, modulePath, options.filename));
34
+
35
+ if (options.createExport !== false) {
36
+ files.push(createModuleResolversExport(modulePath, options.filename));
37
+ }
38
+ }
39
+
40
+ files.push(createPrismaModule(options.root));
41
+
42
+ return files;
43
+ }
44
+
45
+ function createModulePath(
46
+ model: ModuleResolvers,
47
+ rootDir: string,
48
+ namespace: string,
49
+ moduleCasing: Casing
50
+ ) {
51
+ const moduleDir = changeCase(model.name, moduleCasing);
52
+ return join(rootDir, moduleDir, namespace);
53
+ }
54
+
55
+ function createModuleResolvers(model: ModuleResolvers, dir: string, filename: string) {
56
+ const filepath = join(dir, filename);
57
+ const content = printModule(model);
58
+ return new File(filepath, content, tag);
59
+ }
60
+
61
+ function createModuleResolversExport(dir: string, filename: string) {
62
+ const filepath = join(dir, 'index.ts');
63
+ const content = printModuleExport(filename);
64
+ return new File(filepath, content, tag);
65
+ }
66
+
67
+ function createPrismaModule(dir: string) {
68
+ return new File(join(dir, 'prisma/resolvers.ts'), printPrismaModule(), tag);
69
+ }
@@ -0,0 +1,80 @@
1
+ import { concat, mergeDeepWith } from 'ramda';
2
+ import { Store } from '../context';
3
+ import { GenerateOptions } from '../generator';
4
+ import { findModelByOperationSchema, ModelOperations } from '../model-operations';
5
+ import { ModelRelation } from '../model-relations';
6
+ import { createVisitorBuilder } from '../visitor';
7
+ import { createModuleFiles } from './resolver-builder-files';
8
+
9
+ export interface ModuleResolvers {
10
+ name: string;
11
+ queries: ModelOperations[];
12
+ mutations: ModelOperations[];
13
+ relations: ModelRelation[];
14
+ }
15
+
16
+ export type ModuleResolversMap = Record<string, ModuleResolvers | undefined>;
17
+
18
+ export function createResolversBuilder(store: Store, options: GenerateOptions) {
19
+ const { operationsMap, relationsMap } = store;
20
+
21
+ const modelDefinitions: ModuleResolversMap = {};
22
+
23
+ function pushDefinition(model: string, data: Partial<ModuleResolvers>) {
24
+ const current: ModuleResolvers = modelDefinitions[model] ?? {
25
+ name: model,
26
+ queries: [],
27
+ mutations: [],
28
+ relations: [],
29
+ };
30
+ modelDefinitions[model] = mergeDeepWith(concat, current, data);
31
+ }
32
+
33
+ const visitor = createVisitorBuilder();
34
+
35
+ visitor.onModel((model) => {
36
+ const relations = relationsMap[model.name];
37
+ if (!relations) {
38
+ return;
39
+ }
40
+
41
+ pushDefinition(model.name, {
42
+ relations,
43
+ });
44
+ });
45
+
46
+ visitor.onOutputType((outputType) => {
47
+ for (const field of outputType.fields) {
48
+ const type = outputType.name;
49
+ const model = findModelByOperationSchema(operationsMap, field);
50
+ const operations = operationsMap[field.name];
51
+
52
+ if (!model || !operations) {
53
+ continue;
54
+ }
55
+
56
+ if (type === 'Query') {
57
+ pushDefinition(model, { queries: [operations] });
58
+ }
59
+
60
+ if (type === 'Mutation') {
61
+ pushDefinition(model, { mutations: [operations] });
62
+ }
63
+ }
64
+ });
65
+
66
+ function compose() {
67
+ return createModuleFiles(modelDefinitions, {
68
+ root: options.modulesDir,
69
+ casing: options.casing,
70
+ filename: 'resolvers.ts',
71
+ namespace: options.resolverNamespace,
72
+ createExport: options.resolverExport,
73
+ });
74
+ }
75
+
76
+ return {
77
+ compose,
78
+ visitor,
79
+ };
80
+ }
@@ -0,0 +1 @@
1
+ export * from './schema-builder';
@@ -0,0 +1,150 @@
1
+ import { File } from '@baeta/plugin';
2
+ import { join } from 'path';
3
+ import { Casing, changeCase } from '../../utils/casing';
4
+ import { filterEmpty } from '../../utils/string';
5
+ import { printObject } from '../print/object';
6
+ import {
7
+ GlobalSchemaDefinition,
8
+ ModuleDefinitionMap,
9
+ ModuleSchemaDefinition,
10
+ } from './schema-builder';
11
+
12
+ const tag = 'prisma-sdl';
13
+
14
+ export interface SchemaFileOptions {
15
+ root: string;
16
+ casing: Casing;
17
+ namespace: string;
18
+ }
19
+
20
+ export function createGlobalSchemas(
21
+ definition: GlobalSchemaDefinition,
22
+ options: SchemaFileOptions
23
+ ) {
24
+ const dir = options.root + '/prisma';
25
+
26
+ const files = [
27
+ createSchemaFile(
28
+ dir,
29
+ createName('prisma', 'enums', options.casing),
30
+ definition.enums
31
+ ),
32
+ createSchemaFile(
33
+ dir,
34
+ createName('prisma', 'inputs', options.casing),
35
+ definition.inputTypes
36
+ ),
37
+ createSchemaFile(
38
+ dir,
39
+ createName('prisma', 'outputs', options.casing),
40
+ definition.outputTypes
41
+ ),
42
+ createSchemaFile(
43
+ dir,
44
+ createName('prisma', 'scalars', options.casing),
45
+ definition.scalars
46
+ ),
47
+ createSchemaFile(
48
+ dir,
49
+ createName('prisma', 'directives', options.casing),
50
+ definition.directives
51
+ ),
52
+ ];
53
+
54
+ return filterEmpty(files);
55
+ }
56
+
57
+ export function createModuleSchemas(
58
+ definitionMap: ModuleDefinitionMap,
59
+ options: SchemaFileOptions
60
+ ) {
61
+ const definitions = Object.values(definitionMap);
62
+ return definitions.map((definition) => createModuleSchema(definition, options)).flat();
63
+ }
64
+
65
+ function createModuleSchema(
66
+ definition: ModuleSchemaDefinition | undefined,
67
+ options: SchemaFileOptions
68
+ ) {
69
+ if (!definition) {
70
+ return [];
71
+ }
72
+
73
+ const name = definition.name;
74
+ const dir = createSchemaPath(
75
+ definition,
76
+ options.root,
77
+ options.namespace,
78
+ options.casing
79
+ );
80
+
81
+ const files = [
82
+ createSchemaFile(dir, createName(name, 'model', options.casing), definition.model),
83
+ createSchemaFile(dir, createName(name, 'enums', options.casing), definition.enums),
84
+ createSchemaFile(
85
+ dir,
86
+ createName(name, 'input', options.casing),
87
+ definition.inputTypes
88
+ ),
89
+ createSchemaFile(
90
+ dir,
91
+ createName(name, 'output', options.casing),
92
+ definition.outputTypes
93
+ ),
94
+ createSDLOperationFile(
95
+ 'Query',
96
+ dir,
97
+ createName(name, 'query', options.casing),
98
+ definition.queries
99
+ ),
100
+ createSDLOperationFile(
101
+ 'Mutation',
102
+ dir,
103
+ createName(name, 'mutation', options.casing),
104
+ definition.mutations
105
+ ),
106
+ ];
107
+
108
+ return filterEmpty(files);
109
+ }
110
+
111
+ function createSDLOperationFile(
112
+ type: string,
113
+ dir: string,
114
+ name: string,
115
+ content: string[]
116
+ ) {
117
+ if (content.length === 0) {
118
+ return;
119
+ }
120
+ const fileContent = printObject('type', type, content);
121
+ return createSchemaFile(dir, name, fileContent);
122
+ }
123
+
124
+ function createSchemaFile(dir: string, name: string, content: string | string[]) {
125
+ if (content.length === 0) {
126
+ return;
127
+ }
128
+ const filepath = join(dir, name);
129
+ const fileContent = Array.isArray(content) ? content.join('\n\n') : content;
130
+ return new File(filepath, fileContent, tag);
131
+ }
132
+
133
+ export function createSchemaPath(
134
+ definition: ModuleSchemaDefinition,
135
+ rootDir: string,
136
+ namespace: string,
137
+ moduleCasing: Casing
138
+ ) {
139
+ const moduleDir = changeCase(definition.name, moduleCasing);
140
+ return join(rootDir, moduleDir, namespace);
141
+ }
142
+
143
+ function createName(name: string, suffix?: string, casing: Casing = 'kebab-case') {
144
+ const names = [changeCase(name, casing)];
145
+ if (suffix) {
146
+ names.push(changeCase(suffix, casing));
147
+ }
148
+ names.push('gql');
149
+ return names.join('.');
150
+ }
@@ -0,0 +1,3 @@
1
+ export function isOperation(name: string): name is 'Query' | 'Mutation' {
2
+ return ['Query', 'Mutation'].includes(name);
3
+ }
@@ -0,0 +1,186 @@
1
+ import { buildSchema, validateSchema } from 'graphql';
2
+ import { concat, mergeDeepWith } from 'ramda';
3
+ import { Store } from '../context';
4
+ import { DMMF } from '../dmmf';
5
+ import { GenerateOptions } from '../generator';
6
+ import { findModelByEnum } from '../model-enum';
7
+ import { findModelByInput } from '../model-inputs';
8
+ import { findModelByOperationSchema } from '../model-operations';
9
+ import { findModelByOutput } from '../model-outputs';
10
+ import { directives, printDirectives } from '../print/directive';
11
+ import { printPrismaEnum } from '../print/prisma-enum';
12
+ import { printPrismaEnumModel } from '../print/prisma-enum-model';
13
+ import { printPrismaInput } from '../print/prisma-input';
14
+ import { printPrismaModel } from '../print/prisma-model';
15
+ import { printPrismaOperation, printPrismaOutput } from '../print/prisma-output';
16
+ import { printScalars, scalars } from '../print/type';
17
+ import { createVisitorBuilder } from '../visitor';
18
+ import { createGlobalSchemas, createModuleSchemas } from './schema-builder-files';
19
+ import { isOperation } from './schema-builder-utils';
20
+
21
+ export interface GlobalSchemaDefinition {
22
+ enums: string[];
23
+ scalars: string[];
24
+ directives: string[];
25
+ inputTypes: string[];
26
+ outputTypes: string[];
27
+ }
28
+
29
+ export interface ModuleSchemaDefinition {
30
+ name: string;
31
+ model: string[];
32
+ enums: string[];
33
+ queries: string[];
34
+ mutations: string[];
35
+ inputTypes: string[];
36
+ outputTypes: string[];
37
+ }
38
+
39
+ export type ModuleDefinitionMap = Record<string, ModuleSchemaDefinition | undefined>;
40
+
41
+ export function createSchemaBuilder(store: Store, options: GenerateOptions) {
42
+ const { models, inputsMap, outputsMap, operationsMap } = store;
43
+
44
+ const moduleDefinitions: ModuleDefinitionMap = {};
45
+
46
+ let globalDefinitions: GlobalSchemaDefinition = {
47
+ enums: [],
48
+ scalars: scalars,
49
+ directives: directives,
50
+ inputTypes: [],
51
+ outputTypes: [],
52
+ };
53
+
54
+ const schema = [printScalars(), printDirectives()];
55
+
56
+ function pushDefinition(model: string, def: Partial<ModuleSchemaDefinition>) {
57
+ const current = moduleDefinitions[model] ?? {
58
+ name: model,
59
+ model: [],
60
+ queries: [],
61
+ mutations: [],
62
+ inputTypes: [],
63
+ outputTypes: [],
64
+ enums: [],
65
+ };
66
+ moduleDefinitions[model] = mergeDeepWith(concat, current, def);
67
+ }
68
+
69
+ function addToGlobal(def: Partial<GlobalSchemaDefinition>) {
70
+ globalDefinitions = mergeDeepWith(concat, globalDefinitions, def);
71
+ }
72
+
73
+ function addToSchema(value: string) {
74
+ schema.push(value);
75
+ }
76
+
77
+ const visitor = createVisitorBuilder();
78
+
79
+ visitor.onModel((model) => {
80
+ const content = printPrismaModel(model);
81
+ addToSchema(content);
82
+ pushDefinition(model.name, { model: [content] });
83
+ });
84
+
85
+ visitor.onModelEnum((enumModel) => {
86
+ const content = printPrismaEnumModel(enumModel);
87
+ addToSchema(content);
88
+ addToGlobal({ enums: [content] });
89
+ });
90
+
91
+ visitor.onSchemaEnum((enumType) => {
92
+ const content = printPrismaEnum(enumType);
93
+ const model = findModelByEnum(models, enumType);
94
+
95
+ addToSchema(content);
96
+
97
+ if (!model) {
98
+ return addToGlobal({ enums: [content] });
99
+ }
100
+
101
+ pushDefinition(model, { enums: [content] });
102
+ });
103
+
104
+ visitor.onInputType((inputType) => {
105
+ const content = printPrismaInput(inputType);
106
+ const model = findModelByInput(inputsMap, inputType);
107
+
108
+ addToSchema(content);
109
+
110
+ if (!model) {
111
+ return addToGlobal({ inputTypes: [content] });
112
+ }
113
+
114
+ pushDefinition(model, { inputTypes: [content] });
115
+ });
116
+
117
+ visitor.onOutputType((outputType) => {
118
+ const content = printPrismaOutput(outputType);
119
+
120
+ addToSchema(content);
121
+
122
+ if (isOperation(outputType.name)) {
123
+ return handleOperation(outputType, outputType.name);
124
+ }
125
+
126
+ const model = findModelByOutput(outputsMap, outputType);
127
+
128
+ if (!model) {
129
+ return addToGlobal({ outputTypes: [content] });
130
+ }
131
+
132
+ pushDefinition(model, { outputTypes: [content] });
133
+ });
134
+
135
+ function handleOperation(outputType: DMMF.OutputType, operation: 'Query' | 'Mutation') {
136
+ for (const field of outputType.fields) {
137
+ const model = findModelByOperationSchema(operationsMap, field);
138
+
139
+ if (!model) {
140
+ continue;
141
+ }
142
+
143
+ const content = printPrismaOperation(operationsMap, field);
144
+
145
+ if (!content) {
146
+ continue;
147
+ }
148
+
149
+ if (operation === 'Query') {
150
+ pushDefinition(model, {
151
+ queries: [content],
152
+ });
153
+ }
154
+
155
+ if (operation === 'Mutation') {
156
+ pushDefinition(model, {
157
+ mutations: [content],
158
+ });
159
+ }
160
+ }
161
+ }
162
+
163
+ function validate() {
164
+ validateSchema(buildSchema(schema.join('\n')));
165
+ }
166
+
167
+ function compose() {
168
+ validate();
169
+
170
+ const fileOptions = {
171
+ root: options.modulesDir,
172
+ namespace: options.schemaNamespace,
173
+ casing: options.casing,
174
+ };
175
+
176
+ return [
177
+ ...createGlobalSchemas(globalDefinitions, fileOptions),
178
+ ...createModuleSchemas(moduleDefinitions, fileOptions),
179
+ ];
180
+ }
181
+
182
+ return {
183
+ visitor,
184
+ compose,
185
+ };
186
+ }