@axinom/mosaic-graphql-codegen-plugins 0.2.0-rc.0
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.
- package/README.md +16 -0
- package/package.json +44 -0
- package/src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.spec.ts +126 -0
- package/src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.ts +134 -0
- package/src/generate-enum-comments/generate-enum-comments.spec.ts +185 -0
- package/src/generate-enum-comments/generate-enum-comments.ts +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# @axinom/mosaic-graphql-codegen-plugins
|
|
2
|
+
|
|
3
|
+
## About the Package
|
|
4
|
+
|
|
5
|
+
This package is part of the Axinom Mosaic development platform. More information
|
|
6
|
+
can be found at https://portal.axinom.com/mosaic.
|
|
7
|
+
|
|
8
|
+
## License
|
|
9
|
+
|
|
10
|
+
This package can be licensed under the
|
|
11
|
+
[Axinom Products Licensing Agreement](https://portal.axinom.com/mosaic/contracts/products-licensing-agreement)
|
|
12
|
+
or evaluated under the
|
|
13
|
+
[Axinom Products Evaluation Agreement](https://portal.axinom.com/mosaic/contracts/products-evaluation-agreement).
|
|
14
|
+
No part of Axinom's software may be copied, modified, propagated, or distributed
|
|
15
|
+
except in accordance with the terms contained in the Axinom Products Licensing
|
|
16
|
+
Agreement and Axinom Products Evaluation Agreement.
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axinom/mosaic-graphql-codegen-plugins",
|
|
3
|
+
"version": "0.2.0-rc.0",
|
|
4
|
+
"description": "Library of graphql-codegen plugins for Mosaic workflows",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"clean": "rimraf dist",
|
|
7
|
+
"build": "yarn clean && tsc --project tsconfig.build.json",
|
|
8
|
+
"test": "jest --silent",
|
|
9
|
+
"lint": "eslint . --ext .ts,.tsx,.js --color --cache"
|
|
10
|
+
},
|
|
11
|
+
"author": "Axinom",
|
|
12
|
+
"license": "PROPRIETARY",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"axinom",
|
|
15
|
+
"mosaic",
|
|
16
|
+
"axinom mosaic"
|
|
17
|
+
],
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
"./generate-enum-comments": "./dist/generate-enum-comments/generate-enum-comments.js",
|
|
24
|
+
"./generate-bulk-edit-ui-config": "./dist/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"change-case-all": "^2.1.0",
|
|
28
|
+
"handlebars": "^4.7.7"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@graphql-codegen/plugin-helpers": "^5.1.1",
|
|
32
|
+
"jest": "^29",
|
|
33
|
+
"rimraf": "^3.0.2",
|
|
34
|
+
"ts-node": "^10.9.1",
|
|
35
|
+
"typescript": "^5.0.4"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"gitHead": "4d2dbe509438f1c3220e7bbc4aaba013da327d67"
|
|
44
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GraphQLInputObjectType,
|
|
3
|
+
GraphQLObjectType,
|
|
4
|
+
GraphQLSchema,
|
|
5
|
+
GraphQLString,
|
|
6
|
+
} from 'graphql';
|
|
7
|
+
import { BulkEditPluginConfig, plugin } from './generate-bulk-edit-ui-config';
|
|
8
|
+
|
|
9
|
+
describe('generate-bulk-edit-ui-config plugin', () => {
|
|
10
|
+
let schema: GraphQLSchema;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
const RelatedEntitiesInputType = new GraphQLInputObjectType({
|
|
14
|
+
name: 'RelatedEntitiesInput',
|
|
15
|
+
fields: {
|
|
16
|
+
id: { type: GraphQLString },
|
|
17
|
+
name: { type: GraphQLString },
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const SetInputType = new GraphQLInputObjectType({
|
|
22
|
+
name: 'SetInput',
|
|
23
|
+
fields: {
|
|
24
|
+
status: { type: GraphQLString },
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const MutationType = new GraphQLObjectType({
|
|
29
|
+
name: 'Mutation',
|
|
30
|
+
fields: {
|
|
31
|
+
bulkEditEntity: {
|
|
32
|
+
type: GraphQLString,
|
|
33
|
+
args: {
|
|
34
|
+
relatedEntitiesToAdd: { type: RelatedEntitiesInputType },
|
|
35
|
+
relatedEntitiesToRemove: { type: RelatedEntitiesInputType },
|
|
36
|
+
set: { type: SetInputType },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
anotherMutation: {
|
|
40
|
+
type: GraphQLString,
|
|
41
|
+
args: {
|
|
42
|
+
set: { type: SetInputType },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const QueryType = new GraphQLObjectType({
|
|
49
|
+
name: 'Query',
|
|
50
|
+
fields: {
|
|
51
|
+
dummy: { type: GraphQLString },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
schema = new GraphQLSchema({
|
|
56
|
+
query: QueryType,
|
|
57
|
+
mutation: MutationType,
|
|
58
|
+
types: [RelatedEntitiesInputType, SetInputType],
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should generate config for mutations with relatedEntitiesToAdd, relatedEntitiesToRemove, and set', () => {
|
|
63
|
+
const result = plugin(schema, [], {});
|
|
64
|
+
expect(result).toContain('BulkEditEntityFormFieldsConfig');
|
|
65
|
+
expect(result).toContain('relatedEntitiesToAdd');
|
|
66
|
+
expect(result).toContain('relatedEntitiesToRemove');
|
|
67
|
+
expect(result).toContain('set');
|
|
68
|
+
expect(result).toContain('fields');
|
|
69
|
+
expect(result).toContain('mutation');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should generate config for mutations with only set argument', () => {
|
|
73
|
+
const result = plugin(schema, [], {});
|
|
74
|
+
expect(result).toContain('AnotherMutationFormFieldsConfig');
|
|
75
|
+
expect(result).toContain('set');
|
|
76
|
+
expect(result).toContain('fields');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should use custom keys from config', () => {
|
|
80
|
+
const customConfig: Partial<BulkEditPluginConfig> = {
|
|
81
|
+
addKey: 'customAdd',
|
|
82
|
+
removeKey: 'customRemove',
|
|
83
|
+
setKey: 'customSet',
|
|
84
|
+
filterKey: 'customFilter',
|
|
85
|
+
};
|
|
86
|
+
const result = plugin(schema, [], customConfig);
|
|
87
|
+
expect(result).toContain('customAdd');
|
|
88
|
+
expect(result).toContain('customRemove');
|
|
89
|
+
expect(result).toContain('customSet');
|
|
90
|
+
expect(result).toContain('customFilter');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should return empty string if mutationType or queryType is missing', () => {
|
|
94
|
+
const schemaWithoutMutation = new GraphQLSchema({
|
|
95
|
+
query: new GraphQLObjectType({
|
|
96
|
+
name: 'Query',
|
|
97
|
+
fields: { dummy: { type: GraphQLString } },
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
const result = plugin(schemaWithoutMutation, [], {});
|
|
101
|
+
expect(result).toBe('');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should not generate config if mutation has no relevant args', () => {
|
|
105
|
+
const MutationType = new GraphQLObjectType({
|
|
106
|
+
name: 'Mutation',
|
|
107
|
+
fields: {
|
|
108
|
+
noRelevantArgs: {
|
|
109
|
+
type: GraphQLString,
|
|
110
|
+
args: {
|
|
111
|
+
irrelevant: { type: GraphQLString },
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const schemaWithIrrelevantMutation = new GraphQLSchema({
|
|
117
|
+
query: new GraphQLObjectType({
|
|
118
|
+
name: 'Query',
|
|
119
|
+
fields: { dummy: { type: GraphQLString } },
|
|
120
|
+
}),
|
|
121
|
+
mutation: MutationType,
|
|
122
|
+
});
|
|
123
|
+
const result = plugin(schemaWithIrrelevantMutation, [], {});
|
|
124
|
+
expect(result).not.toContain('NoRelevantArgsFormFieldsConfig');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { PluginFunction } from '@graphql-codegen/plugin-helpers';
|
|
3
|
+
import { capitalCase, pascalCase } from 'change-case-all';
|
|
4
|
+
import { GraphQLFieldMap, GraphQLList, GraphQLObjectType } from 'graphql';
|
|
5
|
+
|
|
6
|
+
export interface BulkEditPluginConfig {
|
|
7
|
+
addKey?: string;
|
|
8
|
+
removeKey?: string;
|
|
9
|
+
setKey?: string;
|
|
10
|
+
filterKey?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Data {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const plugin: PluginFunction<Partial<BulkEditPluginConfig>> = (
|
|
18
|
+
schema,
|
|
19
|
+
_documents,
|
|
20
|
+
config,
|
|
21
|
+
) => {
|
|
22
|
+
const addKey = config.addKey || 'relatedEntitiesToAdd';
|
|
23
|
+
const removeKey = config.removeKey || 'relatedEntitiesToRemove';
|
|
24
|
+
const setKey = config.setKey || 'set';
|
|
25
|
+
const filterKey = config.filterKey || 'filter';
|
|
26
|
+
|
|
27
|
+
const mutationType = schema.getMutationType();
|
|
28
|
+
const queryType = schema.getQueryType();
|
|
29
|
+
|
|
30
|
+
if (!mutationType || !queryType) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.warn('No mutation type or query type found in schema');
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const typesMap = schema.getTypeMap();
|
|
37
|
+
|
|
38
|
+
const mutations = mutationType.getFields();
|
|
39
|
+
const configs = Object.keys(mutations).map((mutationName) => {
|
|
40
|
+
const mutation = mutations[mutationName];
|
|
41
|
+
|
|
42
|
+
let formFieldsConfig = {};
|
|
43
|
+
|
|
44
|
+
mutation.args.map((arg) => {
|
|
45
|
+
switch (arg.name.toString()) {
|
|
46
|
+
case 'relatedEntitiesToAdd':
|
|
47
|
+
formFieldsConfig = {
|
|
48
|
+
...formFieldsConfig,
|
|
49
|
+
...resolveFields(
|
|
50
|
+
(typesMap[arg.type.toString()] as GraphQLObjectType).getFields(),
|
|
51
|
+
addKey,
|
|
52
|
+
'Add',
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
break;
|
|
56
|
+
case 'relatedEntitiesToRemove':
|
|
57
|
+
formFieldsConfig = {
|
|
58
|
+
...formFieldsConfig,
|
|
59
|
+
...resolveFields(
|
|
60
|
+
(typesMap[arg.type.toString()] as GraphQLObjectType).getFields(),
|
|
61
|
+
removeKey,
|
|
62
|
+
'Remove',
|
|
63
|
+
),
|
|
64
|
+
};
|
|
65
|
+
break;
|
|
66
|
+
case 'set':
|
|
67
|
+
formFieldsConfig = {
|
|
68
|
+
...formFieldsConfig,
|
|
69
|
+
...resolveFields(
|
|
70
|
+
(typesMap[arg.type.toString()] as GraphQLObjectType).getFields(),
|
|
71
|
+
setKey,
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (Object.keys(formFieldsConfig).length > 0) {
|
|
79
|
+
return `export const ${pascalCase(
|
|
80
|
+
mutationName,
|
|
81
|
+
)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', set: '${setKey}', filter: '${filterKey}' }, fields: ${JSON.stringify(
|
|
82
|
+
formFieldsConfig,
|
|
83
|
+
null,
|
|
84
|
+
2,
|
|
85
|
+
)}};`;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return ['/** Bulk Edit Configurations **/', ...configs]
|
|
90
|
+
.filter((config) => config !== null && config !== undefined)
|
|
91
|
+
.join('\n');
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
function resolveFields(
|
|
95
|
+
fields: GraphQLFieldMap<any, any>,
|
|
96
|
+
action: string,
|
|
97
|
+
postfix = '',
|
|
98
|
+
): Data {
|
|
99
|
+
const resultingFields: Data = {};
|
|
100
|
+
Object.keys(fields).map((fieldName) => {
|
|
101
|
+
const field = fields[fieldName];
|
|
102
|
+
|
|
103
|
+
let type: string | Data[] = field.type.toString();
|
|
104
|
+
|
|
105
|
+
if (Object.keys(field.type).includes('ofType')) {
|
|
106
|
+
const compositeType: Data[] = [];
|
|
107
|
+
|
|
108
|
+
Object.keys((field.type as GraphQLList<any>).ofType.getFields()).forEach(
|
|
109
|
+
(key) => {
|
|
110
|
+
const name = (field.type as GraphQLList<any>).ofType.getFields()[key]
|
|
111
|
+
.name;
|
|
112
|
+
const type = (field.type as GraphQLList<any>).ofType.getFields()[key]
|
|
113
|
+
.type;
|
|
114
|
+
compositeType.push({
|
|
115
|
+
[name]: type,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
type = compositeType;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
resultingFields[postfix ? `${fieldName}${postfix}` : fieldName] = {
|
|
124
|
+
type: type,
|
|
125
|
+
label: postfix
|
|
126
|
+
? `${capitalCase(fieldName)} (${postfix})`
|
|
127
|
+
: capitalCase(fieldName),
|
|
128
|
+
originalFieldName: fieldName,
|
|
129
|
+
action: action,
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return resultingFields;
|
|
134
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { buildSchema } from 'graphql';
|
|
2
|
+
import { plugin } from './generate-enum-comments';
|
|
3
|
+
|
|
4
|
+
describe('generate-enum-comments plugin', () => {
|
|
5
|
+
describe('plugin function', () => {
|
|
6
|
+
it('should generate enum labels for enums with descriptions', () => {
|
|
7
|
+
const schema = buildSchema(`
|
|
8
|
+
enum Status {
|
|
9
|
+
"""Active status"""
|
|
10
|
+
ACTIVE
|
|
11
|
+
"""Inactive status"""
|
|
12
|
+
INACTIVE
|
|
13
|
+
"""Pending status"""
|
|
14
|
+
PENDING
|
|
15
|
+
}
|
|
16
|
+
`);
|
|
17
|
+
|
|
18
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
19
|
+
|
|
20
|
+
expect(result).toContain('export const StatusLabel');
|
|
21
|
+
expect(result).toContain("'ACTIVE' : 'Active status'");
|
|
22
|
+
expect(result).toContain("'INACTIVE' : 'Inactive status'");
|
|
23
|
+
expect(result).toContain("'PENDING' : 'Pending status'");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should handle enums with mixed descriptions', () => {
|
|
27
|
+
const schema = buildSchema(`
|
|
28
|
+
enum Priority {
|
|
29
|
+
"""High priority task"""
|
|
30
|
+
HIGH
|
|
31
|
+
MEDIUM
|
|
32
|
+
"""Low priority task"""
|
|
33
|
+
LOW
|
|
34
|
+
}
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
38
|
+
|
|
39
|
+
expect(result).toContain('export const PriorityLabel');
|
|
40
|
+
expect(result).toContain("'HIGH' : 'High priority task'");
|
|
41
|
+
expect(result).not.toContain("'MEDIUM' :");
|
|
42
|
+
expect(result).toContain("'LOW' : 'Low priority task'");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle enums without descriptions', () => {
|
|
46
|
+
const schema = buildSchema(`
|
|
47
|
+
enum Color {
|
|
48
|
+
RED
|
|
49
|
+
GREEN
|
|
50
|
+
BLUE
|
|
51
|
+
}
|
|
52
|
+
`);
|
|
53
|
+
|
|
54
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
55
|
+
|
|
56
|
+
expect(result).toContain('export const ColorLabel');
|
|
57
|
+
expect(result).not.toContain("'RED' :");
|
|
58
|
+
expect(result).not.toContain("'GREEN' :");
|
|
59
|
+
expect(result).not.toContain("'BLUE' :");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle multiple enums', () => {
|
|
63
|
+
const schema = buildSchema(`
|
|
64
|
+
enum Status {
|
|
65
|
+
"""Active"""
|
|
66
|
+
ACTIVE
|
|
67
|
+
"""Inactive"""
|
|
68
|
+
INACTIVE
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
enum Role {
|
|
72
|
+
"""Administrator role"""
|
|
73
|
+
ADMIN
|
|
74
|
+
"""User role"""
|
|
75
|
+
USER
|
|
76
|
+
}
|
|
77
|
+
`);
|
|
78
|
+
|
|
79
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
80
|
+
|
|
81
|
+
expect(result).toContain('export const StatusLabel');
|
|
82
|
+
expect(result).toContain('export const RoleLabel');
|
|
83
|
+
expect(result).toContain("'ACTIVE' : 'Active'");
|
|
84
|
+
expect(result).toContain("'ADMIN' : 'Administrator role'");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should filter out internal GraphQL types', () => {
|
|
88
|
+
const schema = buildSchema(`
|
|
89
|
+
enum Status {
|
|
90
|
+
ACTIVE
|
|
91
|
+
INACTIVE
|
|
92
|
+
}
|
|
93
|
+
`);
|
|
94
|
+
|
|
95
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
96
|
+
|
|
97
|
+
expect(result).toContain('export const StatusLabel');
|
|
98
|
+
expect(result).not.toContain('__Schema');
|
|
99
|
+
expect(result).not.toContain('__Type');
|
|
100
|
+
expect(result).not.toContain('__Directive');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should handle descriptions with quotes and whitespace', () => {
|
|
104
|
+
const schema = buildSchema(`
|
|
105
|
+
enum Status {
|
|
106
|
+
"""This is a 'quoted' description with multiple spaces"""
|
|
107
|
+
ACTIVE
|
|
108
|
+
}
|
|
109
|
+
`);
|
|
110
|
+
|
|
111
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
112
|
+
|
|
113
|
+
expect(result).toContain(
|
|
114
|
+
"'ACTIVE' : 'This is a \\'quoted\\' description with multiple spaces'",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should generate proxy handler code', () => {
|
|
119
|
+
const schema = buildSchema(`
|
|
120
|
+
enum Status {
|
|
121
|
+
ACTIVE
|
|
122
|
+
}
|
|
123
|
+
`);
|
|
124
|
+
|
|
125
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
126
|
+
|
|
127
|
+
expect(result).toContain('const getWithFallbackHandler');
|
|
128
|
+
expect(result).toContain('ProxyHandler');
|
|
129
|
+
expect(result).toContain('new Proxy');
|
|
130
|
+
expect(result).toContain('getWithFallbackHandler');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should handle empty schema', () => {
|
|
134
|
+
const schema = buildSchema(`
|
|
135
|
+
type Query {
|
|
136
|
+
hello: String
|
|
137
|
+
}
|
|
138
|
+
`);
|
|
139
|
+
|
|
140
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
141
|
+
|
|
142
|
+
expect(result).toContain('/** generate-enum-comments **/');
|
|
143
|
+
expect(result).toContain('const getWithFallbackHandler');
|
|
144
|
+
expect(result).not.toContain('export const');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should handle schema with only scalar and object types', () => {
|
|
148
|
+
const schema = buildSchema(`
|
|
149
|
+
type User {
|
|
150
|
+
id: ID!
|
|
151
|
+
name: String!
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type Query {
|
|
155
|
+
user: User
|
|
156
|
+
}
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
160
|
+
|
|
161
|
+
expect(result).toContain('/** generate-enum-comments **/');
|
|
162
|
+
expect(result).not.toContain('export const');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should generate valid TypeScript code structure', () => {
|
|
166
|
+
const schema = buildSchema(`
|
|
167
|
+
enum Status {
|
|
168
|
+
"""Active status"""
|
|
169
|
+
ACTIVE
|
|
170
|
+
}
|
|
171
|
+
`);
|
|
172
|
+
|
|
173
|
+
const result = plugin(schema, [], {}, { outputFile: '' });
|
|
174
|
+
|
|
175
|
+
// Check for proper TypeScript syntax
|
|
176
|
+
expect(result).toMatch(
|
|
177
|
+
/export const \w+Label = new Proxy<Record<string,string>>/,
|
|
178
|
+
);
|
|
179
|
+
expect(result).toContain(
|
|
180
|
+
'ProxyHandler<{ [key: string | symbol]: string}>',
|
|
181
|
+
);
|
|
182
|
+
expect(result).toContain('target.hasOwnProperty(name)');
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PluginFunction, Types } from '@graphql-codegen/plugin-helpers';
|
|
2
|
+
import { GraphQLNamedType, GraphQLSchema } from 'graphql';
|
|
3
|
+
import handlebars from 'handlebars';
|
|
4
|
+
|
|
5
|
+
handlebars.registerHelper('jsonSafe', (/** @type {String} */ value) => {
|
|
6
|
+
return value.replace(/'/g, "\\'").replace(/\s+/g, ' ');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const template = handlebars.compile(`
|
|
10
|
+
/** generate-enum-comments **/
|
|
11
|
+
const getWithFallbackHandler: ProxyHandler<{ [key: string | symbol]: string}> = {
|
|
12
|
+
get: function(target, name) {
|
|
13
|
+
return target.hasOwnProperty(name) ? target[name] : name;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
{{#each enums}}
|
|
17
|
+
|
|
18
|
+
export const {{{name}}}Label = new Proxy<Record<string,string>>({
|
|
19
|
+
{{#each _values}}
|
|
20
|
+
{{#if description}}
|
|
21
|
+
'{{{name}}}' : '{{{jsonSafe description}}}',
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{/each}}
|
|
24
|
+
}, getWithFallbackHandler);
|
|
25
|
+
{{/each}}
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
function getEnumTypeMap(schema: GraphQLSchema): GraphQLNamedType[] {
|
|
29
|
+
const typeMap = schema.getTypeMap();
|
|
30
|
+
const result: GraphQLNamedType[] = [];
|
|
31
|
+
for (const typeName in typeMap) {
|
|
32
|
+
const type = typeMap[typeName];
|
|
33
|
+
if (
|
|
34
|
+
type.constructor.name === 'GraphQLEnumType' &&
|
|
35
|
+
!typeName.startsWith('__') // Filter out internal types
|
|
36
|
+
) {
|
|
37
|
+
result.push(type);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const plugin: PluginFunction<Types.ConfiguredOutput> = (schema) => {
|
|
44
|
+
const enums = getEnumTypeMap(schema);
|
|
45
|
+
|
|
46
|
+
const result = template({
|
|
47
|
+
enums,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return result;
|
|
51
|
+
};
|