@axinom/mosaic-graphql-codegen-plugins 0.9.0 → 0.10.0-rc.10
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/dist/cjs/codegen-plugin.d.ts +22 -0
- package/dist/cjs/codegen-plugin.js +3 -0
- package/dist/cjs/codegen-plugin.js.map +1 -0
- package/dist/{generate-bulk-edit-ui-config → cjs/generate-bulk-edit-ui-config}/generate-bulk-edit-ui-config.d.ts +2 -2
- package/dist/{generate-bulk-edit-ui-config → cjs/generate-bulk-edit-ui-config}/generate-bulk-edit-ui-config.js +40 -7
- package/dist/cjs/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js.map +1 -0
- package/dist/{generate-bulk-edit-ui-config → cjs/generate-bulk-edit-ui-config}/model.d.ts +7 -0
- package/dist/{generate-bulk-edit-ui-config → cjs/generate-bulk-edit-ui-config}/model.js +1 -0
- package/dist/cjs/generate-bulk-edit-ui-config/model.js.map +1 -0
- package/dist/cjs/generate-enum-comments/generate-enum-comments.d.ts +2 -0
- package/dist/{generate-enum-comments → cjs/generate-enum-comments}/generate-enum-comments.js +5 -2
- package/dist/cjs/generate-enum-comments/generate-enum-comments.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/codegen-plugin.d.ts +22 -0
- package/dist/esm/codegen-plugin.js +2 -0
- package/dist/esm/codegen-plugin.js.map +1 -0
- package/dist/esm/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.d.ts +9 -0
- package/dist/esm/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js +130 -0
- package/dist/esm/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js.map +1 -0
- package/dist/esm/generate-bulk-edit-ui-config/model.d.ts +31 -0
- package/dist/esm/generate-bulk-edit-ui-config/model.js +2 -0
- package/dist/esm/generate-bulk-edit-ui-config/model.js.map +1 -0
- package/dist/esm/generate-enum-comments/generate-enum-comments.d.ts +2 -0
- package/dist/esm/generate-enum-comments/generate-enum-comments.js +45 -0
- package/dist/esm/generate-enum-comments/generate-enum-comments.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/package.json +18 -8
- package/src/codegen-plugin.ts +32 -0
- package/src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.spec.ts +4 -1
- package/src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.ts +44 -9
- package/src/generate-bulk-edit-ui-config/model.ts +7 -0
- package/src/generate-enum-comments/generate-enum-comments.spec.ts +1 -1
- package/src/generate-enum-comments/generate-enum-comments.ts +14 -4
- package/dist/generate-enum-comments/generate-enum-comments.d.ts +0 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GraphQLSchema } from 'graphql';
|
|
2
|
+
export type CodegenPluginOutput = string | {
|
|
3
|
+
content: string;
|
|
4
|
+
prepend?: string[];
|
|
5
|
+
append?: string[];
|
|
6
|
+
meta?: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export interface CodegenPluginInfo {
|
|
9
|
+
outputFile?: string;
|
|
10
|
+
allPlugins?: unknown[];
|
|
11
|
+
pluginContext?: Record<string, unknown>;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Minimal public GraphQL Code Generator plugin signature.
|
|
16
|
+
*
|
|
17
|
+
* The upstream GraphQL Code Generator helper type imports optional loader
|
|
18
|
+
* declarations that are not needed by consumers of this package. Keeping the
|
|
19
|
+
* exported declarations local prevents a dev-only helper package from becoming
|
|
20
|
+
* part of the published type contract.
|
|
21
|
+
*/
|
|
22
|
+
export type CodegenPluginFunction<TConfig = Record<string, unknown>> = (schema: GraphQLSchema, documents: unknown[], config: TConfig, info?: CodegenPluginInfo) => CodegenPluginOutput | Promise<CodegenPluginOutput>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen-plugin.js","sourceRoot":"","sources":["../../src/codegen-plugin.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CodegenPluginFunction } from '../codegen-plugin.js';
|
|
2
2
|
export interface BulkEditPluginConfig {
|
|
3
3
|
addKey?: string;
|
|
4
4
|
removeKey?: string;
|
|
@@ -6,4 +6,4 @@ export interface BulkEditPluginConfig {
|
|
|
6
6
|
setKey?: string;
|
|
7
7
|
filterKey?: string;
|
|
8
8
|
}
|
|
9
|
-
export declare const plugin:
|
|
9
|
+
export declare const plugin: CodegenPluginFunction<Partial<BulkEditPluginConfig>>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.plugin = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
5
|
const change_case_all_1 = require("change-case-all");
|
|
6
|
+
const graphql_1 = require("graphql");
|
|
5
7
|
const plugin = (schema, _documents, config) => {
|
|
6
8
|
const addKey = config.addKey || 'relatedEntitiesToAdd';
|
|
7
9
|
const removeKey = config.removeKey || 'relatedEntitiesToRemove';
|
|
@@ -23,13 +25,22 @@ const plugin = (schema, _documents, config) => {
|
|
|
23
25
|
mutation.args.map((arg) => {
|
|
24
26
|
switch (arg.name.toString()) {
|
|
25
27
|
case 'relatedEntitiesToAdd':
|
|
26
|
-
formFieldsConfig =
|
|
28
|
+
formFieldsConfig = {
|
|
29
|
+
...formFieldsConfig,
|
|
30
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), addKey, 'Add'),
|
|
31
|
+
};
|
|
27
32
|
break;
|
|
28
33
|
case 'relatedEntitiesToRemove':
|
|
29
|
-
formFieldsConfig =
|
|
34
|
+
formFieldsConfig = {
|
|
35
|
+
...formFieldsConfig,
|
|
36
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), removeKey, 'Remove'),
|
|
37
|
+
};
|
|
30
38
|
break;
|
|
31
39
|
case 'set':
|
|
32
|
-
formFieldsConfig =
|
|
40
|
+
formFieldsConfig = {
|
|
41
|
+
...formFieldsConfig,
|
|
42
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), setKey),
|
|
43
|
+
};
|
|
33
44
|
break;
|
|
34
45
|
}
|
|
35
46
|
});
|
|
@@ -40,8 +51,15 @@ const plugin = (schema, _documents, config) => {
|
|
|
40
51
|
formFieldsConfig = resolveClearValues(typesMap[clearArg.type.toString()].getFields(), formFieldsConfig, removeKey);
|
|
41
52
|
}
|
|
42
53
|
}
|
|
54
|
+
// Collect the names of enum-backed fields on the filter input type, so the
|
|
55
|
+
// mutation generator can emit their values as bare GraphQL enum literals
|
|
56
|
+
// (e.g. `status: { in: [ACTIVE] }`) instead of quoted strings.
|
|
57
|
+
const filterArg = mutation.args.find((a) => a.name === filterKey);
|
|
58
|
+
const filterEnumFields = filterArg
|
|
59
|
+
? resolveFilterEnumFields((0, graphql_1.getNamedType)(filterArg.type))
|
|
60
|
+
: [];
|
|
43
61
|
if (Object.keys(formFieldsConfig).length > 0) {
|
|
44
|
-
return `export const ${(0, change_case_all_1.pascalCase)(mutationName)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', clear: '${clearKey}', set: '${setKey}', filter: '${filterKey}' }, fields: ${JSON.stringify(formFieldsConfig, null, 2)}};`;
|
|
62
|
+
return `export const ${(0, change_case_all_1.pascalCase)(mutationName)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', clear: '${clearKey}', set: '${setKey}', filter: '${filterKey}' }, filterEnumFields: ${JSON.stringify(filterEnumFields)}, fields: ${JSON.stringify(formFieldsConfig, null, 2)}};`;
|
|
45
63
|
}
|
|
46
64
|
});
|
|
47
65
|
return ['/** Bulk Edit Configurations **/', ...configs]
|
|
@@ -52,12 +70,11 @@ exports.plugin = plugin;
|
|
|
52
70
|
function resolveFields(fields, action, postfix = '') {
|
|
53
71
|
const resultingFields = {};
|
|
54
72
|
Object.keys(fields).map((fieldName) => {
|
|
55
|
-
var _a, _b;
|
|
56
73
|
const field = fields[fieldName];
|
|
57
74
|
let type = field.type.toString();
|
|
58
75
|
try {
|
|
59
76
|
// Handle list of composite types
|
|
60
|
-
const fields =
|
|
77
|
+
const fields = field.type.ofType?.getFields?.();
|
|
61
78
|
if (fields && typeof fields === 'object') {
|
|
62
79
|
const compositeType = {};
|
|
63
80
|
Object.keys(fields).forEach((key) => {
|
|
@@ -85,9 +102,24 @@ function resolveFields(fields, action, postfix = '') {
|
|
|
85
102
|
});
|
|
86
103
|
return resultingFields;
|
|
87
104
|
}
|
|
105
|
+
// Returns the names of the filter fields whose condition type is backed by an
|
|
106
|
+
// enum (e.g. `status` -> `UserStatusFilter` -> `in: [UserStatus]`). Logical
|
|
107
|
+
// fields (`and`/`or`/`not`) and relation filters resolve to filter input types
|
|
108
|
+
// that have no direct enum member, so they are naturally excluded.
|
|
109
|
+
function resolveFilterEnumFields(filterType) {
|
|
110
|
+
if (!(0, graphql_1.isInputObjectType)(filterType)) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const fields = filterType.getFields();
|
|
114
|
+
return Object.keys(fields).filter((fieldName) => {
|
|
115
|
+
const conditionType = (0, graphql_1.getNamedType)(fields[fieldName].type);
|
|
116
|
+
return ((0, graphql_1.isInputObjectType)(conditionType) &&
|
|
117
|
+
Object.values(conditionType.getFields()).some((member) => (0, graphql_1.isEnumType)((0, graphql_1.getNamedType)(member.type))));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
88
120
|
// If relatedEntitiesToClear contains a field make the clearable field true on the corresponding relatedEntitiesToRemove field.
|
|
89
121
|
function resolveClearValues(fields, fieldMap, action) {
|
|
90
|
-
const resultingFields =
|
|
122
|
+
const resultingFields = { ...fieldMap };
|
|
91
123
|
Object.keys(fields).forEach((fieldName) => {
|
|
92
124
|
Object.keys(fieldMap).map((fieldMapFieldName) => {
|
|
93
125
|
const field = fieldMap[fieldMapFieldName];
|
|
@@ -99,3 +131,4 @@ function resolveClearValues(fields, fieldMap, action) {
|
|
|
99
131
|
});
|
|
100
132
|
return resultingFields;
|
|
101
133
|
}
|
|
134
|
+
//# sourceMappingURL=generate-bulk-edit-ui-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-bulk-edit-ui-config.js","sourceRoot":"","sources":["../../../src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,5 +20,12 @@ export interface BulkEditConfig {
|
|
|
20
20
|
set: string;
|
|
21
21
|
filter: string;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Names of the filter fields whose values are backed by a GraphQL enum.
|
|
25
|
+
* Their values are wrapped so they are emitted as bare enum literals
|
|
26
|
+
* (e.g. `status: { in: [ACTIVE] }`) rather than quoted strings when the
|
|
27
|
+
* filter is inlined into the generated mutation document.
|
|
28
|
+
*/
|
|
29
|
+
filterEnumFields?: string[];
|
|
23
30
|
fields: BulkEditFieldConfigMap;
|
|
24
31
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/generate-bulk-edit-ui-config/model.ts"],"names":[],"mappings":""}
|
package/dist/{generate-enum-comments → cjs/generate-enum-comments}/generate-enum-comments.js
RENAMED
|
@@ -5,10 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.plugin = void 0;
|
|
7
7
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
-
handlebars_1.default.
|
|
8
|
+
const handlebars = (handlebars_1.default.default ??
|
|
9
|
+
handlebars_1.default);
|
|
10
|
+
handlebars.registerHelper('jsonSafe', (/** @type {String} */ value) => {
|
|
9
11
|
return value.replace(/'/g, "\\'").replace(/\s+/g, ' ');
|
|
10
12
|
});
|
|
11
|
-
const template =
|
|
13
|
+
const template = handlebars.compile(`
|
|
12
14
|
/** generate-enum-comments **/
|
|
13
15
|
const getWithFallbackHandler: ProxyHandler<{ [key: string | symbol]: string}> = {
|
|
14
16
|
get: function(target, name) {
|
|
@@ -47,3 +49,4 @@ const plugin = (schema) => {
|
|
|
47
49
|
return result;
|
|
48
50
|
};
|
|
49
51
|
exports.plugin = plugin;
|
|
52
|
+
//# sourceMappingURL=generate-enum-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-enum-comments.js","sourceRoot":"","sources":["../../../src/generate-enum-comments/generate-enum-comments.ts"],"names":[],"mappings":";;;;;;AACA,4DAAqC;AAUrC,MAAM,UAAU,GAAG,CAAE,oBAAiC,CAAC,OAAO;IAC5D,oBAAW,CAAuB,CAAC;AAErC,UAAU,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,EAAE;IACpE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;CAiBnC,CAAC,CAAC;AAEH,SAAS,cAAc,CAAC,MAAqB;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACpC,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/B,IACE,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,iBAAiB;YAC3C,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,4BAA4B;UACvD,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAEM,MAAM,MAAM,GAA0B,CAAC,MAAM,EAAE,EAAE;IACtD,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,QAAQ,CAAC;QACtB,KAAK;KACN,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AARW,QAAA,MAAM,UAQjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GraphQLSchema } from 'graphql';
|
|
2
|
+
export type CodegenPluginOutput = string | {
|
|
3
|
+
content: string;
|
|
4
|
+
prepend?: string[];
|
|
5
|
+
append?: string[];
|
|
6
|
+
meta?: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export interface CodegenPluginInfo {
|
|
9
|
+
outputFile?: string;
|
|
10
|
+
allPlugins?: unknown[];
|
|
11
|
+
pluginContext?: Record<string, unknown>;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Minimal public GraphQL Code Generator plugin signature.
|
|
16
|
+
*
|
|
17
|
+
* The upstream GraphQL Code Generator helper type imports optional loader
|
|
18
|
+
* declarations that are not needed by consumers of this package. Keeping the
|
|
19
|
+
* exported declarations local prevents a dev-only helper package from becoming
|
|
20
|
+
* part of the published type contract.
|
|
21
|
+
*/
|
|
22
|
+
export type CodegenPluginFunction<TConfig = Record<string, unknown>> = (schema: GraphQLSchema, documents: unknown[], config: TConfig, info?: CodegenPluginInfo) => CodegenPluginOutput | Promise<CodegenPluginOutput>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen-plugin.js","sourceRoot":"","sources":["../../src/codegen-plugin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CodegenPluginFunction } from '../codegen-plugin.js';
|
|
2
|
+
export interface BulkEditPluginConfig {
|
|
3
|
+
addKey?: string;
|
|
4
|
+
removeKey?: string;
|
|
5
|
+
clearKey?: string;
|
|
6
|
+
setKey?: string;
|
|
7
|
+
filterKey?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const plugin: CodegenPluginFunction<Partial<BulkEditPluginConfig>>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { capitalCase, pascalCase } from 'change-case-all';
|
|
3
|
+
import { getNamedType, isEnumType, isInputObjectType, } from 'graphql';
|
|
4
|
+
export const plugin = (schema, _documents, config) => {
|
|
5
|
+
const addKey = config.addKey || 'relatedEntitiesToAdd';
|
|
6
|
+
const removeKey = config.removeKey || 'relatedEntitiesToRemove';
|
|
7
|
+
const clearKey = config.clearKey || 'relatedEntitiesToClear';
|
|
8
|
+
const setKey = config.setKey || 'set';
|
|
9
|
+
const filterKey = config.filterKey || 'filter';
|
|
10
|
+
const mutationType = schema.getMutationType();
|
|
11
|
+
const queryType = schema.getQueryType();
|
|
12
|
+
if (!mutationType || !queryType) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.warn('No mutation type or query type found in schema');
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
const typesMap = schema.getTypeMap();
|
|
18
|
+
const mutations = mutationType.getFields();
|
|
19
|
+
const configs = Object.keys(mutations).map((mutationName) => {
|
|
20
|
+
const mutation = mutations[mutationName];
|
|
21
|
+
let formFieldsConfig = {};
|
|
22
|
+
mutation.args.map((arg) => {
|
|
23
|
+
switch (arg.name.toString()) {
|
|
24
|
+
case 'relatedEntitiesToAdd':
|
|
25
|
+
formFieldsConfig = {
|
|
26
|
+
...formFieldsConfig,
|
|
27
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), addKey, 'Add'),
|
|
28
|
+
};
|
|
29
|
+
break;
|
|
30
|
+
case 'relatedEntitiesToRemove':
|
|
31
|
+
formFieldsConfig = {
|
|
32
|
+
...formFieldsConfig,
|
|
33
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), removeKey, 'Remove'),
|
|
34
|
+
};
|
|
35
|
+
break;
|
|
36
|
+
case 'set':
|
|
37
|
+
formFieldsConfig = {
|
|
38
|
+
...formFieldsConfig,
|
|
39
|
+
...resolveFields(typesMap[arg.type.toString()].getFields(), setKey),
|
|
40
|
+
};
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// Check for related entities to clear and set clearable and clearValue accordingly
|
|
45
|
+
if (mutation.args.some((a) => a.name === clearKey)) {
|
|
46
|
+
const clearArg = mutation.args.find((a) => a.name === clearKey);
|
|
47
|
+
if (clearArg) {
|
|
48
|
+
formFieldsConfig = resolveClearValues(typesMap[clearArg.type.toString()].getFields(), formFieldsConfig, removeKey);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Collect the names of enum-backed fields on the filter input type, so the
|
|
52
|
+
// mutation generator can emit their values as bare GraphQL enum literals
|
|
53
|
+
// (e.g. `status: { in: [ACTIVE] }`) instead of quoted strings.
|
|
54
|
+
const filterArg = mutation.args.find((a) => a.name === filterKey);
|
|
55
|
+
const filterEnumFields = filterArg
|
|
56
|
+
? resolveFilterEnumFields(getNamedType(filterArg.type))
|
|
57
|
+
: [];
|
|
58
|
+
if (Object.keys(formFieldsConfig).length > 0) {
|
|
59
|
+
return `export const ${pascalCase(mutationName)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', clear: '${clearKey}', set: '${setKey}', filter: '${filterKey}' }, filterEnumFields: ${JSON.stringify(filterEnumFields)}, fields: ${JSON.stringify(formFieldsConfig, null, 2)}};`;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return ['/** Bulk Edit Configurations **/', ...configs]
|
|
63
|
+
.filter((config) => config !== null && config !== undefined)
|
|
64
|
+
.join('\n');
|
|
65
|
+
};
|
|
66
|
+
function resolveFields(fields, action, postfix = '') {
|
|
67
|
+
const resultingFields = {};
|
|
68
|
+
Object.keys(fields).map((fieldName) => {
|
|
69
|
+
const field = fields[fieldName];
|
|
70
|
+
let type = field.type.toString();
|
|
71
|
+
try {
|
|
72
|
+
// Handle list of composite types
|
|
73
|
+
const fields = field.type.ofType?.getFields?.();
|
|
74
|
+
if (fields && typeof fields === 'object') {
|
|
75
|
+
const compositeType = {};
|
|
76
|
+
Object.keys(fields).forEach((key) => {
|
|
77
|
+
const name = fields[key].name;
|
|
78
|
+
const type = fields[key].type;
|
|
79
|
+
compositeType[name] = type;
|
|
80
|
+
});
|
|
81
|
+
type = [compositeType];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// If we encounter an unknown type structure, fall back to toString()
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.warn(`Warning: Unable to resolve type structure for field "${fieldName}". Using toString() representation.`, error);
|
|
88
|
+
type = field.type.toString();
|
|
89
|
+
}
|
|
90
|
+
resultingFields[postfix ? `${fieldName}${postfix}` : fieldName] = {
|
|
91
|
+
type: type,
|
|
92
|
+
label: postfix
|
|
93
|
+
? `${capitalCase(fieldName)} (${postfix})`
|
|
94
|
+
: capitalCase(fieldName),
|
|
95
|
+
originalFieldName: fieldName,
|
|
96
|
+
action: action,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
return resultingFields;
|
|
100
|
+
}
|
|
101
|
+
// Returns the names of the filter fields whose condition type is backed by an
|
|
102
|
+
// enum (e.g. `status` -> `UserStatusFilter` -> `in: [UserStatus]`). Logical
|
|
103
|
+
// fields (`and`/`or`/`not`) and relation filters resolve to filter input types
|
|
104
|
+
// that have no direct enum member, so they are naturally excluded.
|
|
105
|
+
function resolveFilterEnumFields(filterType) {
|
|
106
|
+
if (!isInputObjectType(filterType)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const fields = filterType.getFields();
|
|
110
|
+
return Object.keys(fields).filter((fieldName) => {
|
|
111
|
+
const conditionType = getNamedType(fields[fieldName].type);
|
|
112
|
+
return (isInputObjectType(conditionType) &&
|
|
113
|
+
Object.values(conditionType.getFields()).some((member) => isEnumType(getNamedType(member.type))));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// If relatedEntitiesToClear contains a field make the clearable field true on the corresponding relatedEntitiesToRemove field.
|
|
117
|
+
function resolveClearValues(fields, fieldMap, action) {
|
|
118
|
+
const resultingFields = { ...fieldMap };
|
|
119
|
+
Object.keys(fields).forEach((fieldName) => {
|
|
120
|
+
Object.keys(fieldMap).map((fieldMapFieldName) => {
|
|
121
|
+
const field = fieldMap[fieldMapFieldName];
|
|
122
|
+
if (field.originalFieldName === fieldName && field.action === action) {
|
|
123
|
+
resultingFields[fieldMapFieldName].clearable = true;
|
|
124
|
+
resultingFields[fieldMapFieldName].clearValue = [];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
return resultingFields;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=generate-bulk-edit-ui-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-bulk-edit-ui-config.js","sourceRoot":"","sources":["../../../src/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface BulkEditFieldConfig {
|
|
2
|
+
type: string | {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
}[];
|
|
5
|
+
label: string;
|
|
6
|
+
originalFieldName: string;
|
|
7
|
+
action: string;
|
|
8
|
+
clearable?: boolean;
|
|
9
|
+
clearValue?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface BulkEditFieldConfigMap {
|
|
12
|
+
[key: string]: BulkEditFieldConfig;
|
|
13
|
+
}
|
|
14
|
+
export interface BulkEditConfig {
|
|
15
|
+
mutation: string;
|
|
16
|
+
keys?: {
|
|
17
|
+
add: string;
|
|
18
|
+
remove: string;
|
|
19
|
+
clear: string;
|
|
20
|
+
set: string;
|
|
21
|
+
filter: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Names of the filter fields whose values are backed by a GraphQL enum.
|
|
25
|
+
* Their values are wrapped so they are emitted as bare enum literals
|
|
26
|
+
* (e.g. `status: { in: [ACTIVE] }`) rather than quoted strings when the
|
|
27
|
+
* filter is inlined into the generated mutation document.
|
|
28
|
+
*/
|
|
29
|
+
filterEnumFields?: string[];
|
|
30
|
+
fields: BulkEditFieldConfigMap;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/generate-bulk-edit-ui-config/model.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import _handlebars from 'handlebars';
|
|
2
|
+
const handlebars = (_handlebars.default ??
|
|
3
|
+
_handlebars);
|
|
4
|
+
handlebars.registerHelper('jsonSafe', (/** @type {String} */ value) => {
|
|
5
|
+
return value.replace(/'/g, "\\'").replace(/\s+/g, ' ');
|
|
6
|
+
});
|
|
7
|
+
const template = handlebars.compile(`
|
|
8
|
+
/** generate-enum-comments **/
|
|
9
|
+
const getWithFallbackHandler: ProxyHandler<{ [key: string | symbol]: string}> = {
|
|
10
|
+
get: function(target, name) {
|
|
11
|
+
return target.hasOwnProperty(name) ? target[name] : name;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
{{#each enums}}
|
|
15
|
+
|
|
16
|
+
export const {{{name}}}Label = new Proxy<Record<string,string>>({
|
|
17
|
+
{{#each _values}}
|
|
18
|
+
{{#if description}}
|
|
19
|
+
'{{{name}}}' : '{{{jsonSafe description}}}',
|
|
20
|
+
{{/if}}
|
|
21
|
+
{{/each}}
|
|
22
|
+
}, getWithFallbackHandler);
|
|
23
|
+
{{/each}}
|
|
24
|
+
`);
|
|
25
|
+
function getEnumTypeMap(schema) {
|
|
26
|
+
const typeMap = schema.getTypeMap();
|
|
27
|
+
const result = [];
|
|
28
|
+
for (const typeName in typeMap) {
|
|
29
|
+
const type = typeMap[typeName];
|
|
30
|
+
if (type.constructor.name === 'GraphQLEnumType' &&
|
|
31
|
+
!typeName.startsWith('__') // Filter out internal types
|
|
32
|
+
) {
|
|
33
|
+
result.push(type);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
export const plugin = (schema) => {
|
|
39
|
+
const enums = getEnumTypeMap(schema);
|
|
40
|
+
const result = template({
|
|
41
|
+
enums,
|
|
42
|
+
});
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=generate-enum-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-enum-comments.js","sourceRoot":"","sources":["../../../src/generate-enum-comments/generate-enum-comments.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,YAAY,CAAC;AAUrC,MAAM,UAAU,GAAG,CAAE,WAAiC,CAAC,OAAO;IAC5D,WAAW,CAAuB,CAAC;AAErC,UAAU,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,EAAE;IACpE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;CAiBnC,CAAC,CAAC;AAEH,SAAS,cAAc,CAAC,MAAqB;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACpC,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/B,IACE,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,iBAAiB;YAC3C,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,4BAA4B;UACvD,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAA0B,CAAC,MAAM,EAAE,EAAE;IACtD,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,QAAQ,CAAC;QACtB,KAAK;KACN,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-graphql-codegen-plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-rc.10",
|
|
4
4
|
"description": "Library of graphql-codegen plugins for Mosaic workflows",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "tsc -w",
|
|
6
|
+
"dev": "tsc -w --project tsconfig.build.json",
|
|
7
7
|
"clean": "rimraf dist",
|
|
8
|
-
"build": "yarn clean && tsc --project tsconfig.build.json",
|
|
8
|
+
"build": "yarn clean && tsc --project tsconfig.build.json && tsc --project tsconfig.build.esm.json && node ../../scripts/write-esm-markers.js",
|
|
9
|
+
"ts:validate": "tsc && yarn build && yarn check:esm-interop",
|
|
9
10
|
"build:ci": "yarn workspaces focus && yarn build",
|
|
10
11
|
"test": "vitest run --silent",
|
|
11
12
|
"test:watch": "vitest watch",
|
|
12
13
|
"test:cov": "vitest run --coverage --silent",
|
|
13
|
-
"lint": "eslint . --ext .ts,.tsx,.js --color --cache"
|
|
14
|
+
"lint": "eslint . --ext .ts,.tsx,.js --color --cache",
|
|
15
|
+
"check:esm-interop": "node ./scripts/check-esm-interop.mjs"
|
|
14
16
|
},
|
|
15
17
|
"author": "Axinom",
|
|
16
18
|
"license": "PROPRIETARY",
|
|
@@ -24,15 +26,23 @@
|
|
|
24
26
|
"src"
|
|
25
27
|
],
|
|
26
28
|
"exports": {
|
|
27
|
-
"./generate-enum-comments":
|
|
28
|
-
|
|
29
|
+
"./generate-enum-comments": {
|
|
30
|
+
"types": "./dist/cjs/generate-enum-comments/generate-enum-comments.d.ts",
|
|
31
|
+
"import": "./dist/esm/generate-enum-comments/generate-enum-comments.js",
|
|
32
|
+
"require": "./dist/cjs/generate-enum-comments/generate-enum-comments.js"
|
|
33
|
+
},
|
|
34
|
+
"./generate-bulk-edit-ui-config": {
|
|
35
|
+
"types": "./dist/cjs/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.d.ts",
|
|
36
|
+
"import": "./dist/esm/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js",
|
|
37
|
+
"require": "./dist/cjs/generate-bulk-edit-ui-config/generate-bulk-edit-ui-config.js"
|
|
38
|
+
}
|
|
29
39
|
},
|
|
30
40
|
"dependencies": {
|
|
31
41
|
"change-case-all": "^2.1.0",
|
|
32
42
|
"handlebars": "^4.7.7"
|
|
33
43
|
},
|
|
34
44
|
"devDependencies": {
|
|
35
|
-
"
|
|
45
|
+
"eslint": "^8.35.0",
|
|
36
46
|
"graphql": "^15.0.0",
|
|
37
47
|
"rimraf": "^3.0.2",
|
|
38
48
|
"ts-node": "^10.9.1",
|
|
@@ -45,5 +55,5 @@
|
|
|
45
55
|
"publishConfig": {
|
|
46
56
|
"access": "public"
|
|
47
57
|
},
|
|
48
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "132f0990224c8692fbe73b2560e54b00d4010e66"
|
|
49
59
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { GraphQLSchema } from 'graphql';
|
|
2
|
+
|
|
3
|
+
export type CodegenPluginOutput =
|
|
4
|
+
| string
|
|
5
|
+
| {
|
|
6
|
+
content: string;
|
|
7
|
+
prepend?: string[];
|
|
8
|
+
append?: string[];
|
|
9
|
+
meta?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface CodegenPluginInfo {
|
|
13
|
+
outputFile?: string;
|
|
14
|
+
allPlugins?: unknown[];
|
|
15
|
+
pluginContext?: Record<string, unknown>;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Minimal public GraphQL Code Generator plugin signature.
|
|
21
|
+
*
|
|
22
|
+
* The upstream GraphQL Code Generator helper type imports optional loader
|
|
23
|
+
* declarations that are not needed by consumers of this package. Keeping the
|
|
24
|
+
* exported declarations local prevents a dev-only helper package from becoming
|
|
25
|
+
* part of the published type contract.
|
|
26
|
+
*/
|
|
27
|
+
export type CodegenPluginFunction<TConfig = Record<string, unknown>> = (
|
|
28
|
+
schema: GraphQLSchema,
|
|
29
|
+
documents: unknown[],
|
|
30
|
+
config: TConfig,
|
|
31
|
+
info?: CodegenPluginInfo,
|
|
32
|
+
) => CodegenPluginOutput | Promise<CodegenPluginOutput>;
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
GraphQLString,
|
|
9
9
|
} from 'graphql';
|
|
10
10
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type BulkEditPluginConfig,
|
|
13
|
+
plugin,
|
|
14
|
+
} from './generate-bulk-edit-ui-config.js';
|
|
12
15
|
|
|
13
16
|
describe('generate-bulk-edit-ui-config plugin', () => {
|
|
14
17
|
let schema: GraphQLSchema;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { PluginFunction } from '@graphql-codegen/plugin-helpers';
|
|
3
2
|
import { capitalCase, pascalCase } from 'change-case-all';
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
getNamedType,
|
|
5
|
+
type GraphQLFieldMap,
|
|
6
|
+
type GraphQLInputObjectType,
|
|
7
|
+
type GraphQLList,
|
|
8
|
+
type GraphQLObjectType,
|
|
9
|
+
isEnumType,
|
|
10
|
+
isInputObjectType,
|
|
11
|
+
} from 'graphql';
|
|
12
|
+
import type { CodegenPluginFunction } from '../codegen-plugin.js';
|
|
13
|
+
import type { BulkEditFieldConfigMap } from './model.js';
|
|
6
14
|
|
|
7
15
|
export interface BulkEditPluginConfig {
|
|
8
16
|
addKey?: string;
|
|
@@ -12,7 +20,7 @@ export interface BulkEditPluginConfig {
|
|
|
12
20
|
filterKey?: string;
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
export const plugin:
|
|
23
|
+
export const plugin: CodegenPluginFunction<Partial<BulkEditPluginConfig>> = (
|
|
16
24
|
schema,
|
|
17
25
|
_documents,
|
|
18
26
|
config,
|
|
@@ -87,14 +95,20 @@ export const plugin: PluginFunction<Partial<BulkEditPluginConfig>> = (
|
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
// Collect the names of enum-backed fields on the filter input type, so the
|
|
99
|
+
// mutation generator can emit their values as bare GraphQL enum literals
|
|
100
|
+
// (e.g. `status: { in: [ACTIVE] }`) instead of quoted strings.
|
|
101
|
+
const filterArg = mutation.args.find((a) => a.name === filterKey);
|
|
102
|
+
const filterEnumFields = filterArg
|
|
103
|
+
? resolveFilterEnumFields(getNamedType(filterArg.type))
|
|
104
|
+
: [];
|
|
105
|
+
|
|
90
106
|
if (Object.keys(formFieldsConfig).length > 0) {
|
|
91
107
|
return `export const ${pascalCase(
|
|
92
108
|
mutationName,
|
|
93
|
-
)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', clear: '${clearKey}', set: '${setKey}', filter: '${filterKey}' },
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
2,
|
|
97
|
-
)}};`;
|
|
109
|
+
)}FormFieldsConfig = { mutation: '${mutationName}', keys: { add: '${addKey}', remove: '${removeKey}', clear: '${clearKey}', set: '${setKey}', filter: '${filterKey}' }, filterEnumFields: ${JSON.stringify(
|
|
110
|
+
filterEnumFields,
|
|
111
|
+
)}, fields: ${JSON.stringify(formFieldsConfig, null, 2)}};`;
|
|
98
112
|
}
|
|
99
113
|
});
|
|
100
114
|
|
|
@@ -152,6 +166,27 @@ function resolveFields(
|
|
|
152
166
|
return resultingFields;
|
|
153
167
|
}
|
|
154
168
|
|
|
169
|
+
// Returns the names of the filter fields whose condition type is backed by an
|
|
170
|
+
// enum (e.g. `status` -> `UserStatusFilter` -> `in: [UserStatus]`). Logical
|
|
171
|
+
// fields (`and`/`or`/`not`) and relation filters resolve to filter input types
|
|
172
|
+
// that have no direct enum member, so they are naturally excluded.
|
|
173
|
+
function resolveFilterEnumFields(filterType: unknown): string[] {
|
|
174
|
+
if (!isInputObjectType(filterType)) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const fields = (filterType as GraphQLInputObjectType).getFields();
|
|
179
|
+
return Object.keys(fields).filter((fieldName) => {
|
|
180
|
+
const conditionType = getNamedType(fields[fieldName].type);
|
|
181
|
+
return (
|
|
182
|
+
isInputObjectType(conditionType) &&
|
|
183
|
+
Object.values(conditionType.getFields()).some((member) =>
|
|
184
|
+
isEnumType(getNamedType(member.type)),
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
155
190
|
// If relatedEntitiesToClear contains a field make the clearable field true on the corresponding relatedEntitiesToRemove field.
|
|
156
191
|
function resolveClearValues(
|
|
157
192
|
fields: GraphQLFieldMap<any, any>,
|
|
@@ -21,5 +21,12 @@ export interface BulkEditConfig {
|
|
|
21
21
|
set: string;
|
|
22
22
|
filter: string;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Names of the filter fields whose values are backed by a GraphQL enum.
|
|
26
|
+
* Their values are wrapped so they are emitted as bare enum literals
|
|
27
|
+
* (e.g. `status: { in: [ACTIVE] }`) rather than quoted strings when the
|
|
28
|
+
* filter is inlined into the generated mutation document.
|
|
29
|
+
*/
|
|
30
|
+
filterEnumFields?: string[];
|
|
24
31
|
fields: BulkEditFieldConfigMap;
|
|
25
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildSchema } from 'graphql';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { plugin } from './generate-enum-comments';
|
|
3
|
+
import { plugin } from './generate-enum-comments.js';
|
|
4
4
|
|
|
5
5
|
describe('generate-enum-comments plugin', () => {
|
|
6
6
|
describe('plugin function', () => {
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import type { GraphQLNamedType, GraphQLSchema } from 'graphql';
|
|
2
|
+
import _handlebars from 'handlebars';
|
|
3
|
+
import type { CodegenPluginFunction } from '../codegen-plugin.js';
|
|
4
|
+
// handlebars is a TypeScript-compiled CJS package that emits
|
|
5
|
+
// `module.exports = { __esModule: true, default: value }`.
|
|
6
|
+
// Node.js native ESM does not honour the __esModule sentinel, so the default
|
|
7
|
+
// import receives the whole module.exports wrapper. See Pattern 9 in
|
|
8
|
+
// ESM_MIGRATION_GUIDE.md.
|
|
9
|
+
type HandlebarsInterop = typeof _handlebars & {
|
|
10
|
+
default?: typeof _handlebars;
|
|
11
|
+
};
|
|
12
|
+
const handlebars = ((_handlebars as HandlebarsInterop).default ??
|
|
13
|
+
_handlebars) as typeof _handlebars;
|
|
4
14
|
|
|
5
15
|
handlebars.registerHelper('jsonSafe', (/** @type {String} */ value) => {
|
|
6
16
|
return value.replace(/'/g, "\\'").replace(/\s+/g, ' ');
|
|
@@ -40,7 +50,7 @@ function getEnumTypeMap(schema: GraphQLSchema): GraphQLNamedType[] {
|
|
|
40
50
|
return result;
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
export const plugin:
|
|
53
|
+
export const plugin: CodegenPluginFunction = (schema) => {
|
|
44
54
|
const enums = getEnumTypeMap(schema);
|
|
45
55
|
|
|
46
56
|
const result = template({
|