@comet/admin-generator 8.17.1 → 9.0.0-beta.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/bin/admin-generator.mjs +3 -0
- package/dist/adminGenerator.d.mts +1 -0
- package/dist/adminGenerator.mjs +4441 -0
- package/dist/index.d.mts +344 -0
- package/dist/index.mjs +4440 -0
- package/package.json +31 -21
- package/bin/admin-generator.js +0 -8
- package/lib/adminGenerator.d.ts +0 -1
- package/lib/adminGenerator.js +0 -8
- package/lib/commands/generate/config/parseConfig.d.ts +0 -1
- package/lib/commands/generate/config/parseConfig.js +0 -72
- package/lib/commands/generate/config/transformConfig.d.ts +0 -7
- package/lib/commands/generate/config/transformConfig.js +0 -264
- package/lib/commands/generate/generate-command.d.ts +0 -340
- package/lib/commands/generate/generate-command.js +0 -112
- package/lib/commands/generate/generateForm/asyncSelect/generateAsyncSelect.d.ts +0 -26
- package/lib/commands/generate/generateForm/asyncSelect/generateAsyncSelect.js +0 -364
- package/lib/commands/generate/generateForm/extractErrorEnums.d.ts +0 -16
- package/lib/commands/generate/generateForm/extractErrorEnums.js +0 -100
- package/lib/commands/generate/generateForm/flatFormFieldsFromFormConfig.d.ts +0 -2
- package/lib/commands/generate/generateForm/flatFormFieldsFromFormConfig.js +0 -22
- package/lib/commands/generate/generateForm/formField/findIntrospectionFieldType.d.ts +0 -6
- package/lib/commands/generate/generateForm/formField/findIntrospectionFieldType.js +0 -22
- package/lib/commands/generate/generateForm/formField/options.d.ts +0 -24
- package/lib/commands/generate/generateForm/formField/options.js +0 -85
- package/lib/commands/generate/generateForm/generateComponentFormField.d.ts +0 -5
- package/lib/commands/generate/generateForm/generateComponentFormField.js +0 -22
- package/lib/commands/generate/generateForm/generateErrorHandling.d.ts +0 -8
- package/lib/commands/generate/generateForm/generateErrorHandling.js +0 -24
- package/lib/commands/generate/generateForm/generateErrorMessages.d.ts +0 -11
- package/lib/commands/generate/generateForm/generateErrorMessages.js +0 -28
- package/lib/commands/generate/generateForm/generateFields.d.ts +0 -42
- package/lib/commands/generate/generateForm/generateFields.js +0 -87
- package/lib/commands/generate/generateForm/generateForm.d.ts +0 -14
- package/lib/commands/generate/generateForm/generateForm.js +0 -565
- package/lib/commands/generate/generateForm/generateFormField.d.ts +0 -12
- package/lib/commands/generate/generateForm/generateFormField.js +0 -368
- package/lib/commands/generate/generateForm/generateFormLayout.d.ts +0 -12
- package/lib/commands/generate/generateForm/generateFormLayout.js +0 -154
- package/lib/commands/generate/generateForm/generateFormValues.d.ts +0 -40
- package/lib/commands/generate/generateForm/generateFormValues.js +0 -219
- package/lib/commands/generate/generateForm/generateFragmentByFormFragmentFields.d.ts +0 -11
- package/lib/commands/generate/generateForm/generateFragmentByFormFragmentFields.js +0 -29
- package/lib/commands/generate/generateForm/getForwardedGqlArgs.d.ts +0 -18
- package/lib/commands/generate/generateForm/getForwardedGqlArgs.js +0 -79
- package/lib/commands/generate/generateGrid/detectMuiXVersion.d.ts +0 -5
- package/lib/commands/generate/generateGrid/detectMuiXVersion.js +0 -32
- package/lib/commands/generate/generateGrid/findInputObjectType.d.ts +0 -2
- package/lib/commands/generate/generateGrid/findInputObjectType.js +0 -15
- package/lib/commands/generate/generateGrid/generateGqlFieldList.d.ts +0 -6
- package/lib/commands/generate/generateGrid/generateGqlFieldList.js +0 -56
- package/lib/commands/generate/generateGrid/generateGrid.d.ts +0 -16
- package/lib/commands/generate/generateGrid/generateGrid.js +0 -1017
- package/lib/commands/generate/generateGrid/generateGridToolbar.d.ts +0 -15
- package/lib/commands/generate/generateGrid/generateGridToolbar.js +0 -90
- package/lib/commands/generate/generateGrid/getForwardedGqlArgs.d.ts +0 -13
- package/lib/commands/generate/generateGrid/getForwardedGqlArgs.js +0 -59
- package/lib/commands/generate/generateGrid/getPropsForFilterProp.d.ts +0 -14
- package/lib/commands/generate/generateGrid/getPropsForFilterProp.js +0 -13
- package/lib/commands/generate/generateGrid/usableFields.d.ts +0 -14
- package/lib/commands/generate/generateGrid/usableFields.js +0 -3
- package/lib/commands/generate/utils/camelCaseToHumanReadable.d.ts +0 -1
- package/lib/commands/generate/utils/camelCaseToHumanReadable.js +0 -7
- package/lib/commands/generate/utils/columnVisibility.d.ts +0 -6
- package/lib/commands/generate/utils/columnVisibility.js +0 -2
- package/lib/commands/generate/utils/convertConfigImport.d.ts +0 -6
- package/lib/commands/generate/utils/convertConfigImport.js +0 -14
- package/lib/commands/generate/utils/findMutationType.d.ts +0 -3
- package/lib/commands/generate/utils/findMutationType.js +0 -18
- package/lib/commands/generate/utils/findQueryType.d.ts +0 -2
- package/lib/commands/generate/utils/findQueryType.js +0 -18
- package/lib/commands/generate/utils/findRootBlocks.d.ts +0 -8
- package/lib/commands/generate/utils/findRootBlocks.js +0 -66
- package/lib/commands/generate/utils/generateGqlOperation.d.ts +0 -12
- package/lib/commands/generate/utils/generateGqlOperation.js +0 -87
- package/lib/commands/generate/utils/generateImportsCode.d.ts +0 -6
- package/lib/commands/generate/utils/generateImportsCode.js +0 -31
- package/lib/commands/generate/utils/intl.d.ts +0 -20
- package/lib/commands/generate/utils/intl.js +0 -44
- package/lib/commands/generate/utils/isFieldOptional.d.ts +0 -7
- package/lib/commands/generate/utils/isFieldOptional.js +0 -21
- package/lib/commands/generate/utils/runtimeTypeGuards.d.ts +0 -20
- package/lib/commands/generate/utils/runtimeTypeGuards.js +0 -22
- package/lib/commands/generate/utils/writeGenerated.d.ts +0 -1
- package/lib/commands/generate/utils/writeGenerated.js +0 -123
- package/lib/index.d.ts +0 -2
- package/lib/index.js +0 -6
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,4440 @@
|
|
|
1
|
+
import { GraphQLFileLoader } from "@graphql-tools/graphql-file-loader";
|
|
2
|
+
import { loadSchema } from "@graphql-tools/load";
|
|
3
|
+
import { exec } from "child_process";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import fs, { existsSync, promises, readFileSync } from "fs";
|
|
6
|
+
import { glob } from "glob";
|
|
7
|
+
import { introspectionFromSchema } from "graphql";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { basename, dirname } from "path";
|
|
10
|
+
import { promisify } from "util";
|
|
11
|
+
import { createJiti } from "jiti";
|
|
12
|
+
import * as ts$1 from "typescript";
|
|
13
|
+
import ts from "typescript";
|
|
14
|
+
import { camelCase, capitalCase } from "change-case";
|
|
15
|
+
import { FormattedMessage } from "react-intl";
|
|
16
|
+
import pluralize from "pluralize";
|
|
17
|
+
import objectPath from "object-path";
|
|
18
|
+
|
|
19
|
+
//#region rolldown:runtime
|
|
20
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/commands/generate/config/transformConfig.ts
|
|
24
|
+
const supportedImportPaths = [
|
|
25
|
+
"[type=grid].columns.filterOperators",
|
|
26
|
+
"[type=grid].columns.block",
|
|
27
|
+
"[type=grid].columns.component",
|
|
28
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`),
|
|
29
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.block`),
|
|
30
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.component`)
|
|
31
|
+
];
|
|
32
|
+
const supportedInlineCodePaths = ["[type=grid].columns.renderCell", ...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`)];
|
|
33
|
+
function transformConfigFile(fileName, sourceText) {
|
|
34
|
+
const sourceFile = ts$1.createSourceFile(fileName, sourceText, ts$1.ScriptTarget.ES2024, true);
|
|
35
|
+
const importedIdentifiers = collectImports(sourceFile);
|
|
36
|
+
function configTransformer() {
|
|
37
|
+
return (context) => {
|
|
38
|
+
const visit = (node, path$1) => {
|
|
39
|
+
if (ts$1.isCallExpression(node)) {
|
|
40
|
+
if (node.expression.getText() === "injectFormVariables") {
|
|
41
|
+
if (!path$1.startsWith("[type=form].fields")) throw new Error(`injectFormVariables can only be used in form field definitions: ${path$1}`);
|
|
42
|
+
if (node.arguments.length !== 1) throw new Error(`injectFormVariables expects exactly one argument`);
|
|
43
|
+
const injectFormVariablesArg = node.arguments[0];
|
|
44
|
+
if (!ts$1.isArrowFunction(injectFormVariablesArg)) throw new Error(`injectFormVariables expects an arrow function as its argument`);
|
|
45
|
+
node = injectFormVariablesArg.body;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (ts$1.isArrowFunction(node)) if (supportedInlineCodePaths.includes(path$1)) {
|
|
49
|
+
let code = node.getText();
|
|
50
|
+
if (code.endsWith(",")) code = code.slice(0, -1);
|
|
51
|
+
const imports = findUsedImports(node.body, importedIdentifiers);
|
|
52
|
+
return ts$1.factory.createObjectLiteralExpression([ts$1.factory.createPropertyAssignment("code", ts$1.factory.createStringLiteral(code)), ts$1.factory.createPropertyAssignment("imports", ts$1.factory.createArrayLiteralExpression(imports.map((imprt) => {
|
|
53
|
+
return ts$1.factory.createObjectLiteralExpression([
|
|
54
|
+
ts$1.factory.createPropertyAssignment("name", ts$1.factory.createStringLiteral(imprt.name)),
|
|
55
|
+
ts$1.factory.createPropertyAssignment("import", ts$1.factory.createStringLiteral(imprt.import)),
|
|
56
|
+
...imprt.defaultImport ? [ts$1.factory.createPropertyAssignment("defaultImport", ts$1.factory.createTrue())] : []
|
|
57
|
+
]);
|
|
58
|
+
})))], true);
|
|
59
|
+
} else throw new Error(`Inline Function is not allowed here and calling the function is not supported: ${path$1}`);
|
|
60
|
+
else if (ts$1.isIdentifier(node)) {
|
|
61
|
+
const imported = importedIdentifiers.get(node.text);
|
|
62
|
+
if (imported) {
|
|
63
|
+
if (supportedImportPaths.includes(path$1)) return ts$1.factory.createObjectLiteralExpression([ts$1.factory.createPropertyAssignment("name", ts$1.factory.createStringLiteral(node.text)), ts$1.factory.createPropertyAssignment("import", ts$1.factory.createStringLiteral(imported.import))], true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (![
|
|
67
|
+
ts$1.SyntaxKind.Identifier,
|
|
68
|
+
ts$1.SyntaxKind.ArrayLiteralExpression,
|
|
69
|
+
ts$1.SyntaxKind.ObjectLiteralExpression,
|
|
70
|
+
ts$1.SyntaxKind.TaggedTemplateExpression,
|
|
71
|
+
ts$1.SyntaxKind.SpreadElement,
|
|
72
|
+
ts$1.SyntaxKind.PropertyAssignment,
|
|
73
|
+
ts$1.SyntaxKind.ShorthandPropertyAssignment
|
|
74
|
+
].includes(node.kind)) return node;
|
|
75
|
+
let newPath = path$1;
|
|
76
|
+
if (path$1 == "") {
|
|
77
|
+
if (ts$1.isObjectLiteralExpression(node)) {
|
|
78
|
+
const typeProperty = getTypePropertyFromObjectLiteral(node);
|
|
79
|
+
newPath = typeProperty ? `[type=${typeProperty}]` : "";
|
|
80
|
+
}
|
|
81
|
+
} else if (ts$1.isPropertyAssignment(node)) newPath = `${path$1}.${node.name.getText()}`;
|
|
82
|
+
return ts$1.visitEachChild(node, (child) => {
|
|
83
|
+
return visit(child, newPath);
|
|
84
|
+
}, context);
|
|
85
|
+
};
|
|
86
|
+
return (node) => ts$1.visitNode(node, (child) => visit(child, ""));
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const configNode = findConfigNode(sourceFile);
|
|
90
|
+
const transformedConfigNode = ts$1.transform(configNode, [configTransformer()]).transformed[0];
|
|
91
|
+
const updatedSource = ts$1.transform(sourceFile, [(context) => {
|
|
92
|
+
const visitor = (node) => {
|
|
93
|
+
if (node === configNode) return transformedConfigNode;
|
|
94
|
+
return ts$1.visitEachChild(node, visitor, context);
|
|
95
|
+
};
|
|
96
|
+
return (node) => ts$1.visitNode(node, visitor);
|
|
97
|
+
}]).transformed[0];
|
|
98
|
+
return ts$1.createPrinter().printFile(updatedSource);
|
|
99
|
+
}
|
|
100
|
+
function findConfigNode(sourceFile) {
|
|
101
|
+
let ret;
|
|
102
|
+
sourceFile.forEachChild((node) => {
|
|
103
|
+
if (ts$1.isExportAssignment(node)) {
|
|
104
|
+
const exportedNode = node.expression;
|
|
105
|
+
if (ts$1.isCallExpression(exportedNode) && exportedNode.expression.getText() == "defineConfig") {
|
|
106
|
+
const args = exportedNode.arguments;
|
|
107
|
+
if (args.length != 1) throw new Error(`Expected exactly one argument for defineConfig`);
|
|
108
|
+
ret = args[0];
|
|
109
|
+
return false;
|
|
110
|
+
} else if (ts$1.isSatisfiesExpression(exportedNode)) {
|
|
111
|
+
if (ts$1.isObjectLiteralExpression(exportedNode.expression)) {
|
|
112
|
+
ret = exportedNode.expression;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
} else if (ts$1.isObjectLiteralExpression(exportedNode)) {
|
|
116
|
+
ret = exportedNode;
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (!ret) throw new Error(`No default export found, please export the GeneratorConfig as default, preferrable using defineConfig helper.`);
|
|
122
|
+
return ret;
|
|
123
|
+
}
|
|
124
|
+
function getTypePropertyFromObjectLiteral(node) {
|
|
125
|
+
for (const property of node.properties) if (ts$1.isPropertyAssignment(property)) {
|
|
126
|
+
if (property.name.getText() == "type") {
|
|
127
|
+
const propertyAssignmentInitializer = property.initializer;
|
|
128
|
+
if (ts$1.isStringLiteral(propertyAssignmentInitializer)) return propertyAssignmentInitializer.text;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function collectImports(rootNode) {
|
|
134
|
+
const importedIdentifiers = /* @__PURE__ */ new Map();
|
|
135
|
+
function visit(node) {
|
|
136
|
+
if (ts$1.isImportDeclaration(node) && node.importClause) {
|
|
137
|
+
const moduleSpecifier = node.moduleSpecifier.text;
|
|
138
|
+
if (node.importClause.namedBindings && ts$1.isNamedImports(node.importClause.namedBindings)) for (const element of node.importClause.namedBindings.elements) {
|
|
139
|
+
const localName = element.name.text;
|
|
140
|
+
const originalName = element.propertyName ? element.propertyName.text : localName;
|
|
141
|
+
importedIdentifiers.set(localName, {
|
|
142
|
+
name: originalName,
|
|
143
|
+
import: moduleSpecifier
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else if (node.importClause.name && ts$1.isIdentifier(node.importClause.name)) {
|
|
147
|
+
const localName = node.importClause.name.text;
|
|
148
|
+
importedIdentifiers.set(localName, {
|
|
149
|
+
defaultImport: true,
|
|
150
|
+
name: localName,
|
|
151
|
+
import: moduleSpecifier
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
ts$1.forEachChild(node, visit);
|
|
156
|
+
}
|
|
157
|
+
visit(rootNode);
|
|
158
|
+
return importedIdentifiers;
|
|
159
|
+
}
|
|
160
|
+
function findUsedImports(rootNode, importedIdentifiers) {
|
|
161
|
+
const imports = [];
|
|
162
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
163
|
+
function collectUsedIdentifiers(node) {
|
|
164
|
+
if (ts$1.isIdentifier(node)) usedNames.add(node.text);
|
|
165
|
+
ts$1.forEachChild(node, collectUsedIdentifiers);
|
|
166
|
+
}
|
|
167
|
+
collectUsedIdentifiers(rootNode);
|
|
168
|
+
for (const name of usedNames) {
|
|
169
|
+
const imported = importedIdentifiers.get(name);
|
|
170
|
+
if (imported) imports.push(imported);
|
|
171
|
+
}
|
|
172
|
+
return imports;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/commands/generate/config/parseConfig.ts
|
|
177
|
+
let alias;
|
|
178
|
+
try {
|
|
179
|
+
const tsConfigJson = readFileSync("tsconfig.json", "utf-8");
|
|
180
|
+
const tsConfig = JSON.parse(tsConfigJson);
|
|
181
|
+
if (tsConfig.compilerOptions?.paths) {
|
|
182
|
+
alias = {};
|
|
183
|
+
for (const [key, value] of Object.entries(tsConfig.compilerOptions.paths)) {
|
|
184
|
+
const cleanedKey = key.replace("/*", "");
|
|
185
|
+
const cleanedValue = value[0].replace("/*", "");
|
|
186
|
+
alias[cleanedKey] = `${process.cwd()}/${cleanedValue}`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.warn("Failed to parse TSConfig paths, import aliases might not work. See original error below");
|
|
191
|
+
console.warn(error);
|
|
192
|
+
}
|
|
193
|
+
const jiti = createJiti(import.meta.url, {
|
|
194
|
+
alias,
|
|
195
|
+
fsCache: false,
|
|
196
|
+
jsx: { runtime: "automatic" }
|
|
197
|
+
});
|
|
198
|
+
async function parseConfig(file) {
|
|
199
|
+
const transformedConfig = transformConfigFile(file, await promises.readFile(file, "utf-8"));
|
|
200
|
+
const tempFileName = `${dirname(file)}/.temp-${basename(file)}`;
|
|
201
|
+
await promises.writeFile(tempFileName, transformedConfig, "utf-8");
|
|
202
|
+
let executedConfig;
|
|
203
|
+
try {
|
|
204
|
+
executedConfig = await jiti.import(tempFileName.replace(/\.tsx?$/, ""), { default: true });
|
|
205
|
+
} catch (e) {
|
|
206
|
+
console.error(e);
|
|
207
|
+
throw new Error(`Error executing config file ${file}: ${e}`);
|
|
208
|
+
}
|
|
209
|
+
await promises.rm(tempFileName);
|
|
210
|
+
return executedConfig;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/commands/generate/utils/convertConfigImport.ts
|
|
215
|
+
function convertConfigImport(imprt) {
|
|
216
|
+
let importPath = imprt.import;
|
|
217
|
+
if (importPath.startsWith("../")) importPath = `../${importPath}`;
|
|
218
|
+
else if (importPath.startsWith("./")) importPath = `.${importPath}`;
|
|
219
|
+
return {
|
|
220
|
+
name: imprt.name,
|
|
221
|
+
importPath,
|
|
222
|
+
defaultImport: imprt.defaultImport
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/commands/generate/utils/findMutationType.ts
|
|
228
|
+
function findMutationType(mutationName, schema) {
|
|
229
|
+
if (!schema.__schema.mutationType) throw new Error("Schema has no Mutation type");
|
|
230
|
+
const queryType = schema.__schema.types.find((type) => type.name === schema.__schema.mutationType?.name);
|
|
231
|
+
if (!queryType) throw new Error("Can't find Mutation type in gql schema");
|
|
232
|
+
return queryType.fields.find((field) => field.name === mutationName);
|
|
233
|
+
}
|
|
234
|
+
function findMutationTypeOrThrow(mutationName, schema) {
|
|
235
|
+
const ret = findMutationType(mutationName, schema);
|
|
236
|
+
if (!ret) throw new Error(`Can't find Mutation ${mutationName} in gql schema`);
|
|
237
|
+
return ret;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/commands/generate/utils/generateGqlOperation.ts
|
|
242
|
+
function generateGqlQueryTreeFromFields(fields) {
|
|
243
|
+
function getOrCreateNode(root$1, path$1) {
|
|
244
|
+
let node = root$1;
|
|
245
|
+
for (const part of path$1) {
|
|
246
|
+
if (!node.children[part]) node.children[part] = {
|
|
247
|
+
children: {},
|
|
248
|
+
fragments: []
|
|
249
|
+
};
|
|
250
|
+
node = node.children[part];
|
|
251
|
+
}
|
|
252
|
+
return node;
|
|
253
|
+
}
|
|
254
|
+
const root = {
|
|
255
|
+
children: {},
|
|
256
|
+
fragments: []
|
|
257
|
+
};
|
|
258
|
+
for (const field of fields) {
|
|
259
|
+
const fragmentMatch = field.match(/(.*)(\.{3}.*)/);
|
|
260
|
+
if (fragmentMatch) {
|
|
261
|
+
const key = fragmentMatch[1].trim();
|
|
262
|
+
const fragment = fragmentMatch[2].trim();
|
|
263
|
+
if (key === "") root.fragments.push(fragment);
|
|
264
|
+
else getOrCreateNode(root, key.split(".").filter(Boolean)).fragments.push(fragment);
|
|
265
|
+
} else {
|
|
266
|
+
const path$1 = field.split(".").filter(Boolean);
|
|
267
|
+
if (path$1.length === 0) continue;
|
|
268
|
+
getOrCreateNode(root, path$1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function recursiveStringify$1(node) {
|
|
272
|
+
const parts = [];
|
|
273
|
+
for (const key of Object.keys(node.children)) {
|
|
274
|
+
const child = node.children[key];
|
|
275
|
+
const childStr = recursiveStringify$1(child);
|
|
276
|
+
if (childStr) parts.push(`${key} { ${childStr} }`);
|
|
277
|
+
else parts.push(key);
|
|
278
|
+
}
|
|
279
|
+
if (node.fragments.length > 0) parts.push(...node.fragments);
|
|
280
|
+
return parts.join(" ");
|
|
281
|
+
}
|
|
282
|
+
return recursiveStringify$1(root);
|
|
283
|
+
}
|
|
284
|
+
function generateGqlOperation(options) {
|
|
285
|
+
let queryArgs = "";
|
|
286
|
+
if (options.variables && options.variables.length > 0) {
|
|
287
|
+
queryArgs += `(`;
|
|
288
|
+
queryArgs += options.variables.map((v) => `$${v.name}: ${v.type}`).join(", ");
|
|
289
|
+
queryArgs += `)`;
|
|
290
|
+
}
|
|
291
|
+
let rootQueryArgs = "";
|
|
292
|
+
if (options.variables && options.variables.length > 0) {
|
|
293
|
+
rootQueryArgs += `(`;
|
|
294
|
+
rootQueryArgs += options.variables.map((v) => `${v.name}: $${v.name}`).join(", ");
|
|
295
|
+
rootQueryArgs += `)`;
|
|
296
|
+
}
|
|
297
|
+
return `
|
|
298
|
+
${options.type} ${options.operationName}${queryArgs} {
|
|
299
|
+
${options.rootOperation}${rootQueryArgs} {
|
|
300
|
+
${generateGqlQueryTreeFromFields(options.fields)}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
${(options.fragmentVariables ?? []).join("\n")}
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/commands/generate/utils/generateImportsCode.ts
|
|
309
|
+
function generateImportsCode(imports) {
|
|
310
|
+
const importsNameToPath = /* @__PURE__ */ new Map();
|
|
311
|
+
return imports.filter((imp) => {
|
|
312
|
+
if (importsNameToPath.has(imp.name)) if (importsNameToPath.get(imp.name) !== imp.importPath) throw new Error(`Duplicate import name ${imp.name}`);
|
|
313
|
+
else return false;
|
|
314
|
+
importsNameToPath.set(imp.name, imp.importPath);
|
|
315
|
+
return true;
|
|
316
|
+
}).map((imp) => {
|
|
317
|
+
if (imp.defaultImport) return `import ${imp.name} from "${imp.importPath}";`;
|
|
318
|
+
else return `import { ${imp.name} } from "${imp.importPath}";`;
|
|
319
|
+
}).join("\n");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/commands/generate/utils/runtimeTypeGuards.ts
|
|
324
|
+
/**
|
|
325
|
+
* Type Guard used by generator runtime for places where runtime vs. configtime types mismatch
|
|
326
|
+
*/
|
|
327
|
+
function isGeneratorConfigImport(value) {
|
|
328
|
+
if (value && typeof value === "object" && "import" in value && "name" in value) return true;
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Type Guard used by generator runtime for places where runtime vs. configtime types mismatch
|
|
333
|
+
*/
|
|
334
|
+
function isGeneratorConfigCode(value) {
|
|
335
|
+
if (value && typeof value === "object" && "code" in value && "imports" in value) return true;
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/commands/generate/generateForm/extractErrorEnums.ts
|
|
341
|
+
/**
|
|
342
|
+
* Extracts error enum type names from create and update mutations.
|
|
343
|
+
* Navigates: Mutation → return type → payload object → errors field → LIST → error object → code field → ENUM
|
|
344
|
+
*/
|
|
345
|
+
function extractErrorEnumsFromMutations({ createMutationType, updateMutationType, gqlIntrospection }) {
|
|
346
|
+
const createErrorEnum = createMutationType ? extractErrorEnumFromMutation(createMutationType, gqlIntrospection) : void 0;
|
|
347
|
+
const updateErrorEnum = updateMutationType ? extractErrorEnumFromMutation(updateMutationType, gqlIntrospection) : void 0;
|
|
348
|
+
return {
|
|
349
|
+
createErrorEnum,
|
|
350
|
+
updateErrorEnum,
|
|
351
|
+
useDifferentEnums: Boolean(createErrorEnum && updateErrorEnum && createErrorEnum !== updateErrorEnum)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Extracts error enum type name from a single mutation field.
|
|
356
|
+
* Returns undefined if mutation doesn't have error enum.
|
|
357
|
+
*/
|
|
358
|
+
function extractErrorEnumFromMutation(mutationType, gqlIntrospection) {
|
|
359
|
+
try {
|
|
360
|
+
let returnType = mutationType.type;
|
|
361
|
+
if (returnType.kind === "NON_NULL") returnType = returnType.ofType;
|
|
362
|
+
if (returnType.kind !== "OBJECT") return;
|
|
363
|
+
const payloadType = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === returnType.name);
|
|
364
|
+
if (!payloadType) return;
|
|
365
|
+
const errorsField = payloadType.fields.find((field) => field.name === "errors");
|
|
366
|
+
if (!errorsField) return;
|
|
367
|
+
return extractEnumFromErrorsField(errorsField, gqlIntrospection);
|
|
368
|
+
} catch {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Extracts enum type name from the errors field.
|
|
374
|
+
* Traverses: errors field → unwrap NON_NULL → unwrap LIST → unwrap NON_NULL → error OBJECT → code field → ENUM
|
|
375
|
+
*/
|
|
376
|
+
function extractEnumFromErrorsField(errorsField, gqlIntrospection) {
|
|
377
|
+
try {
|
|
378
|
+
let errorsType = errorsField.type;
|
|
379
|
+
if (errorsType.kind === "NON_NULL") errorsType = errorsType.ofType;
|
|
380
|
+
if (errorsType.kind !== "LIST") return;
|
|
381
|
+
let errorItemType = errorsType.ofType;
|
|
382
|
+
if (errorItemType.kind === "NON_NULL") errorItemType = errorItemType.ofType;
|
|
383
|
+
if (errorItemType.kind !== "OBJECT") return;
|
|
384
|
+
const errorObjectType = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === errorItemType.name);
|
|
385
|
+
if (!errorObjectType) return;
|
|
386
|
+
const codeField = errorObjectType.fields.find((field) => field.name === "code");
|
|
387
|
+
if (!codeField) return;
|
|
388
|
+
let codeType = codeField.type;
|
|
389
|
+
if (codeType.kind === "NON_NULL") codeType = codeType.ofType;
|
|
390
|
+
if (codeType.kind !== "ENUM") return;
|
|
391
|
+
return codeType.name;
|
|
392
|
+
} catch {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/commands/generate/generateForm/flatFormFieldsFromFormConfig.ts
|
|
399
|
+
function flatFormFieldsFromFormConfig(config) {
|
|
400
|
+
return config.fields.reduce((acc, field) => {
|
|
401
|
+
if (isFormLayoutConfig(field)) field.fields.forEach((nestedFieldConfig) => {
|
|
402
|
+
if (isFormFieldConfig(nestedFieldConfig)) acc.push(nestedFieldConfig);
|
|
403
|
+
});
|
|
404
|
+
else if (isFormFieldConfig(field)) acc.push(field);
|
|
405
|
+
return acc;
|
|
406
|
+
}, []);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/commands/generate/generateForm/generateErrorHandling.ts
|
|
411
|
+
/**
|
|
412
|
+
* Generates error handling code for form submissions.
|
|
413
|
+
* Creates errors.reduce() implementation that maps GraphQL errors to Final Form submission errors.
|
|
414
|
+
*/
|
|
415
|
+
function generateErrorHandlingCode({ mutationResponsePath, errorMessagesVariable }) {
|
|
416
|
+
return `
|
|
417
|
+
if (${mutationResponsePath}.errors.length) {
|
|
418
|
+
return ${mutationResponsePath}.errors.reduce(
|
|
419
|
+
(submissionErrors, error) => {
|
|
420
|
+
const errorMessage = ${errorMessagesVariable}[error.code];
|
|
421
|
+
if (error.field) {
|
|
422
|
+
submissionErrors[error.field] = errorMessage;
|
|
423
|
+
} else {
|
|
424
|
+
submissionErrors[FORM_ERROR] = errorMessage;
|
|
425
|
+
}
|
|
426
|
+
return submissionErrors;
|
|
427
|
+
},
|
|
428
|
+
{} as Record<string, ReactNode>,
|
|
429
|
+
);
|
|
430
|
+
}`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/commands/generate/utils/camelCaseToHumanReadable.ts
|
|
435
|
+
function camelCaseToHumanReadable(s) {
|
|
436
|
+
return capitalCase(s);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/commands/generate/generateForm/generateErrorMessages.ts
|
|
441
|
+
/**
|
|
442
|
+
* Generates TypeScript code for submissionErrorMessages object(s).
|
|
443
|
+
* Creates a mapped type object with FormattedMessage components for each error code.
|
|
444
|
+
*/
|
|
445
|
+
function generateErrorMessagesCode({ enumName, gqlType, variableName, gqlIntrospection }) {
|
|
446
|
+
const enumType = gqlIntrospection.__schema.types.find((type) => type.kind === "ENUM" && type.name === enumName);
|
|
447
|
+
if (!enumType || enumType.enumValues.length === 0) return "";
|
|
448
|
+
return `const ${variableName}: Record<GQL${enumName}, ReactNode> = {
|
|
449
|
+
${enumType.enumValues.map((enumValue) => {
|
|
450
|
+
const errorCode = enumValue.name;
|
|
451
|
+
const defaultMessage = enumValue.description || camelCaseToHumanReadable(errorCode);
|
|
452
|
+
return `${errorCode}: <FormattedMessage id="${`${gqlType.toLowerCase()}.form.error.${errorCode}`}" defaultMessage="${defaultMessage}" />`;
|
|
453
|
+
}).join(",\n")},
|
|
454
|
+
};`;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/commands/generate/generateForm/generateComponentFormField.ts
|
|
459
|
+
function generateComponentFormField({ config }) {
|
|
460
|
+
if (!isGeneratorConfigImport(config.component)) throw new Error("config.component must be a GeneratorConfigImport");
|
|
461
|
+
return {
|
|
462
|
+
imports: [convertConfigImport(config.component)],
|
|
463
|
+
formProps: [],
|
|
464
|
+
hooksCode: "",
|
|
465
|
+
formFragmentFields: [],
|
|
466
|
+
formValuesConfig: [],
|
|
467
|
+
finalFormConfig: void 0,
|
|
468
|
+
code: `<${config.component.name} />`,
|
|
469
|
+
gqlDocuments: {}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region ../../../node_modules/.pnpm/react@19.2.4/node_modules/react/cjs/react.production.js
|
|
475
|
+
/**
|
|
476
|
+
* @license React
|
|
477
|
+
* react.production.js
|
|
478
|
+
*
|
|
479
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
480
|
+
*
|
|
481
|
+
* This source code is licensed under the MIT license found in the
|
|
482
|
+
* LICENSE file in the root directory of this source tree.
|
|
483
|
+
*/
|
|
484
|
+
var require_react_production = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
485
|
+
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|
486
|
+
var ReactNoopUpdateQueue = {
|
|
487
|
+
isMounted: function() {
|
|
488
|
+
return !1;
|
|
489
|
+
},
|
|
490
|
+
enqueueForceUpdate: function() {},
|
|
491
|
+
enqueueReplaceState: function() {},
|
|
492
|
+
enqueueSetState: function() {}
|
|
493
|
+
}, assign = Object.assign, emptyObject = {};
|
|
494
|
+
function Component(props, context, updater) {
|
|
495
|
+
this.props = props;
|
|
496
|
+
this.context = context;
|
|
497
|
+
this.refs = emptyObject;
|
|
498
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
499
|
+
}
|
|
500
|
+
Component.prototype.isReactComponent = {};
|
|
501
|
+
Component.prototype.setState = function(partialState, callback) {
|
|
502
|
+
if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState) throw Error("takes an object of state variables to update or a function which returns an object of state variables.");
|
|
503
|
+
this.updater.enqueueSetState(this, partialState, callback, "setState");
|
|
504
|
+
};
|
|
505
|
+
Component.prototype.forceUpdate = function(callback) {
|
|
506
|
+
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
|
|
507
|
+
};
|
|
508
|
+
function ComponentDummy() {}
|
|
509
|
+
ComponentDummy.prototype = Component.prototype;
|
|
510
|
+
function PureComponent(props, context, updater) {
|
|
511
|
+
this.props = props;
|
|
512
|
+
this.context = context;
|
|
513
|
+
this.refs = emptyObject;
|
|
514
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
515
|
+
}
|
|
516
|
+
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
|
|
517
|
+
pureComponentPrototype.constructor = PureComponent;
|
|
518
|
+
assign(pureComponentPrototype, Component.prototype);
|
|
519
|
+
pureComponentPrototype.isPureReactComponent = !0;
|
|
520
|
+
var isArrayImpl = Array.isArray;
|
|
521
|
+
function isValidElement(object) {
|
|
522
|
+
return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
|
|
523
|
+
}
|
|
524
|
+
exports.isValidElement = isValidElement;
|
|
525
|
+
}));
|
|
526
|
+
|
|
527
|
+
//#endregion
|
|
528
|
+
//#region ../../../node_modules/.pnpm/react@19.2.4/node_modules/react/cjs/react.development.js
|
|
529
|
+
/**
|
|
530
|
+
* @license React
|
|
531
|
+
* react.development.js
|
|
532
|
+
*
|
|
533
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
534
|
+
*
|
|
535
|
+
* This source code is licensed under the MIT license found in the
|
|
536
|
+
* LICENSE file in the root directory of this source tree.
|
|
537
|
+
*/
|
|
538
|
+
var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
539
|
+
"production" !== process.env.NODE_ENV && (function() {
|
|
540
|
+
function defineDeprecationWarning(methodName, info) {
|
|
541
|
+
Object.defineProperty(Component.prototype, methodName, { get: function() {
|
|
542
|
+
console.warn("%s(...) is deprecated in plain JavaScript React classes. %s", info[0], info[1]);
|
|
543
|
+
} });
|
|
544
|
+
}
|
|
545
|
+
function getIteratorFn(maybeIterable) {
|
|
546
|
+
if (null === maybeIterable || "object" !== typeof maybeIterable) return null;
|
|
547
|
+
maybeIterable = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable["@@iterator"];
|
|
548
|
+
return "function" === typeof maybeIterable ? maybeIterable : null;
|
|
549
|
+
}
|
|
550
|
+
function warnNoop(publicInstance, callerName) {
|
|
551
|
+
publicInstance = (publicInstance = publicInstance.constructor) && (publicInstance.displayName || publicInstance.name) || "ReactClass";
|
|
552
|
+
var warningKey = publicInstance + "." + callerName;
|
|
553
|
+
didWarnStateUpdateForUnmountedComponent[warningKey] || (console.error("Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.", callerName, publicInstance), didWarnStateUpdateForUnmountedComponent[warningKey] = !0);
|
|
554
|
+
}
|
|
555
|
+
function Component(props, context, updater) {
|
|
556
|
+
this.props = props;
|
|
557
|
+
this.context = context;
|
|
558
|
+
this.refs = emptyObject;
|
|
559
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
560
|
+
}
|
|
561
|
+
function ComponentDummy() {}
|
|
562
|
+
function PureComponent(props, context, updater) {
|
|
563
|
+
this.props = props;
|
|
564
|
+
this.context = context;
|
|
565
|
+
this.refs = emptyObject;
|
|
566
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
567
|
+
}
|
|
568
|
+
function noop() {}
|
|
569
|
+
function testStringCoercion(value) {
|
|
570
|
+
return "" + value;
|
|
571
|
+
}
|
|
572
|
+
function checkKeyStringCoercion(value) {
|
|
573
|
+
try {
|
|
574
|
+
testStringCoercion(value);
|
|
575
|
+
var JSCompiler_inline_result = !1;
|
|
576
|
+
} catch (e) {
|
|
577
|
+
JSCompiler_inline_result = !0;
|
|
578
|
+
}
|
|
579
|
+
if (JSCompiler_inline_result) {
|
|
580
|
+
JSCompiler_inline_result = console;
|
|
581
|
+
var JSCompiler_temp_const = JSCompiler_inline_result.error;
|
|
582
|
+
var JSCompiler_inline_result$jscomp$0 = "function" === typeof Symbol && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object";
|
|
583
|
+
JSCompiler_temp_const.call(JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0);
|
|
584
|
+
return testStringCoercion(value);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function getComponentNameFromType(type) {
|
|
588
|
+
if (null == type) return null;
|
|
589
|
+
if ("function" === typeof type) return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null;
|
|
590
|
+
if ("string" === typeof type) return type;
|
|
591
|
+
switch (type) {
|
|
592
|
+
case REACT_FRAGMENT_TYPE: return "Fragment";
|
|
593
|
+
case REACT_PROFILER_TYPE: return "Profiler";
|
|
594
|
+
case REACT_STRICT_MODE_TYPE: return "StrictMode";
|
|
595
|
+
case REACT_SUSPENSE_TYPE: return "Suspense";
|
|
596
|
+
case REACT_SUSPENSE_LIST_TYPE: return "SuspenseList";
|
|
597
|
+
case REACT_ACTIVITY_TYPE: return "Activity";
|
|
598
|
+
}
|
|
599
|
+
if ("object" === typeof type) switch ("number" === typeof type.tag && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), type.$$typeof) {
|
|
600
|
+
case REACT_PORTAL_TYPE: return "Portal";
|
|
601
|
+
case REACT_CONTEXT_TYPE: return type.displayName || "Context";
|
|
602
|
+
case REACT_CONSUMER_TYPE: return (type._context.displayName || "Context") + ".Consumer";
|
|
603
|
+
case REACT_FORWARD_REF_TYPE:
|
|
604
|
+
var innerType = type.render;
|
|
605
|
+
type = type.displayName;
|
|
606
|
+
type || (type = innerType.displayName || innerType.name || "", type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef");
|
|
607
|
+
return type;
|
|
608
|
+
case REACT_MEMO_TYPE: return innerType = type.displayName || null, null !== innerType ? innerType : getComponentNameFromType(type.type) || "Memo";
|
|
609
|
+
case REACT_LAZY_TYPE:
|
|
610
|
+
innerType = type._payload;
|
|
611
|
+
type = type._init;
|
|
612
|
+
try {
|
|
613
|
+
return getComponentNameFromType(type(innerType));
|
|
614
|
+
} catch (x) {}
|
|
615
|
+
}
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
function getTaskName(type) {
|
|
619
|
+
if (type === REACT_FRAGMENT_TYPE) return "<>";
|
|
620
|
+
if ("object" === typeof type && null !== type && type.$$typeof === REACT_LAZY_TYPE) return "<...>";
|
|
621
|
+
try {
|
|
622
|
+
var name = getComponentNameFromType(type);
|
|
623
|
+
return name ? "<" + name + ">" : "<...>";
|
|
624
|
+
} catch (x) {
|
|
625
|
+
return "<...>";
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function getOwner() {
|
|
629
|
+
var dispatcher = ReactSharedInternals.A;
|
|
630
|
+
return null === dispatcher ? null : dispatcher.getOwner();
|
|
631
|
+
}
|
|
632
|
+
function UnknownOwner() {
|
|
633
|
+
return Error("react-stack-top-frame");
|
|
634
|
+
}
|
|
635
|
+
function hasValidKey(config) {
|
|
636
|
+
if (hasOwnProperty.call(config, "key")) {
|
|
637
|
+
var getter = Object.getOwnPropertyDescriptor(config, "key").get;
|
|
638
|
+
if (getter && getter.isReactWarning) return !1;
|
|
639
|
+
}
|
|
640
|
+
return void 0 !== config.key;
|
|
641
|
+
}
|
|
642
|
+
function defineKeyPropWarningGetter(props, displayName) {
|
|
643
|
+
function warnAboutAccessingKey() {
|
|
644
|
+
specialPropKeyWarningShown || (specialPropKeyWarningShown = !0, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName));
|
|
645
|
+
}
|
|
646
|
+
warnAboutAccessingKey.isReactWarning = !0;
|
|
647
|
+
Object.defineProperty(props, "key", {
|
|
648
|
+
get: warnAboutAccessingKey,
|
|
649
|
+
configurable: !0
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
function elementRefGetterWithDeprecationWarning() {
|
|
653
|
+
var componentName = getComponentNameFromType(this.type);
|
|
654
|
+
didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = !0, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."));
|
|
655
|
+
componentName = this.props.ref;
|
|
656
|
+
return void 0 !== componentName ? componentName : null;
|
|
657
|
+
}
|
|
658
|
+
function ReactElement(type, key, props, owner, debugStack, debugTask) {
|
|
659
|
+
var refProp = props.ref;
|
|
660
|
+
type = {
|
|
661
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
662
|
+
type,
|
|
663
|
+
key,
|
|
664
|
+
props,
|
|
665
|
+
_owner: owner
|
|
666
|
+
};
|
|
667
|
+
null !== (void 0 !== refProp ? refProp : null) ? Object.defineProperty(type, "ref", {
|
|
668
|
+
enumerable: !1,
|
|
669
|
+
get: elementRefGetterWithDeprecationWarning
|
|
670
|
+
}) : Object.defineProperty(type, "ref", {
|
|
671
|
+
enumerable: !1,
|
|
672
|
+
value: null
|
|
673
|
+
});
|
|
674
|
+
type._store = {};
|
|
675
|
+
Object.defineProperty(type._store, "validated", {
|
|
676
|
+
configurable: !1,
|
|
677
|
+
enumerable: !1,
|
|
678
|
+
writable: !0,
|
|
679
|
+
value: 0
|
|
680
|
+
});
|
|
681
|
+
Object.defineProperty(type, "_debugInfo", {
|
|
682
|
+
configurable: !1,
|
|
683
|
+
enumerable: !1,
|
|
684
|
+
writable: !0,
|
|
685
|
+
value: null
|
|
686
|
+
});
|
|
687
|
+
Object.defineProperty(type, "_debugStack", {
|
|
688
|
+
configurable: !1,
|
|
689
|
+
enumerable: !1,
|
|
690
|
+
writable: !0,
|
|
691
|
+
value: debugStack
|
|
692
|
+
});
|
|
693
|
+
Object.defineProperty(type, "_debugTask", {
|
|
694
|
+
configurable: !1,
|
|
695
|
+
enumerable: !1,
|
|
696
|
+
writable: !0,
|
|
697
|
+
value: debugTask
|
|
698
|
+
});
|
|
699
|
+
Object.freeze && (Object.freeze(type.props), Object.freeze(type));
|
|
700
|
+
return type;
|
|
701
|
+
}
|
|
702
|
+
function cloneAndReplaceKey(oldElement, newKey) {
|
|
703
|
+
newKey = ReactElement(oldElement.type, newKey, oldElement.props, oldElement._owner, oldElement._debugStack, oldElement._debugTask);
|
|
704
|
+
oldElement._store && (newKey._store.validated = oldElement._store.validated);
|
|
705
|
+
return newKey;
|
|
706
|
+
}
|
|
707
|
+
function validateChildKeys(node) {
|
|
708
|
+
isValidElement$1(node) ? node._store && (node._store.validated = 1) : "object" === typeof node && null !== node && node.$$typeof === REACT_LAZY_TYPE && ("fulfilled" === node._payload.status ? isValidElement$1(node._payload.value) && node._payload.value._store && (node._payload.value._store.validated = 1) : node._store && (node._store.validated = 1));
|
|
709
|
+
}
|
|
710
|
+
function isValidElement$1(object) {
|
|
711
|
+
return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
|
|
712
|
+
}
|
|
713
|
+
function escape(key) {
|
|
714
|
+
var escaperLookup = {
|
|
715
|
+
"=": "=0",
|
|
716
|
+
":": "=2"
|
|
717
|
+
};
|
|
718
|
+
return "$" + key.replace(/[=:]/g, function(match) {
|
|
719
|
+
return escaperLookup[match];
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
function getElementKey(element, index) {
|
|
723
|
+
return "object" === typeof element && null !== element && null != element.key ? (checkKeyStringCoercion(element.key), escape("" + element.key)) : index.toString(36);
|
|
724
|
+
}
|
|
725
|
+
function resolveThenable(thenable) {
|
|
726
|
+
switch (thenable.status) {
|
|
727
|
+
case "fulfilled": return thenable.value;
|
|
728
|
+
case "rejected": throw thenable.reason;
|
|
729
|
+
default: switch ("string" === typeof thenable.status ? thenable.then(noop, noop) : (thenable.status = "pending", thenable.then(function(fulfilledValue) {
|
|
730
|
+
"pending" === thenable.status && (thenable.status = "fulfilled", thenable.value = fulfilledValue);
|
|
731
|
+
}, function(error) {
|
|
732
|
+
"pending" === thenable.status && (thenable.status = "rejected", thenable.reason = error);
|
|
733
|
+
})), thenable.status) {
|
|
734
|
+
case "fulfilled": return thenable.value;
|
|
735
|
+
case "rejected": throw thenable.reason;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
throw thenable;
|
|
739
|
+
}
|
|
740
|
+
function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
|
|
741
|
+
var type = typeof children;
|
|
742
|
+
if ("undefined" === type || "boolean" === type) children = null;
|
|
743
|
+
var invokeCallback = !1;
|
|
744
|
+
if (null === children) invokeCallback = !0;
|
|
745
|
+
else switch (type) {
|
|
746
|
+
case "bigint":
|
|
747
|
+
case "string":
|
|
748
|
+
case "number":
|
|
749
|
+
invokeCallback = !0;
|
|
750
|
+
break;
|
|
751
|
+
case "object": switch (children.$$typeof) {
|
|
752
|
+
case REACT_ELEMENT_TYPE:
|
|
753
|
+
case REACT_PORTAL_TYPE:
|
|
754
|
+
invokeCallback = !0;
|
|
755
|
+
break;
|
|
756
|
+
case REACT_LAZY_TYPE: return invokeCallback = children._init, mapIntoArray(invokeCallback(children._payload), array, escapedPrefix, nameSoFar, callback);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (invokeCallback) {
|
|
760
|
+
invokeCallback = children;
|
|
761
|
+
callback = callback(invokeCallback);
|
|
762
|
+
var childKey = "" === nameSoFar ? "." + getElementKey(invokeCallback, 0) : nameSoFar;
|
|
763
|
+
isArrayImpl(callback) ? (escapedPrefix = "", null != childKey && (escapedPrefix = childKey.replace(userProvidedKeyEscapeRegex, "$&/") + "/"), mapIntoArray(callback, array, escapedPrefix, "", function(c) {
|
|
764
|
+
return c;
|
|
765
|
+
})) : null != callback && (isValidElement$1(callback) && (null != callback.key && (invokeCallback && invokeCallback.key === callback.key || checkKeyStringCoercion(callback.key)), escapedPrefix = cloneAndReplaceKey(callback, escapedPrefix + (null == callback.key || invokeCallback && invokeCallback.key === callback.key ? "" : ("" + callback.key).replace(userProvidedKeyEscapeRegex, "$&/") + "/") + childKey), "" !== nameSoFar && null != invokeCallback && isValidElement$1(invokeCallback) && null == invokeCallback.key && invokeCallback._store && !invokeCallback._store.validated && (escapedPrefix._store.validated = 2), callback = escapedPrefix), array.push(callback));
|
|
766
|
+
return 1;
|
|
767
|
+
}
|
|
768
|
+
invokeCallback = 0;
|
|
769
|
+
childKey = "" === nameSoFar ? "." : nameSoFar + ":";
|
|
770
|
+
if (isArrayImpl(children)) for (var i = 0; i < children.length; i++) nameSoFar = children[i], type = childKey + getElementKey(nameSoFar, i), invokeCallback += mapIntoArray(nameSoFar, array, escapedPrefix, type, callback);
|
|
771
|
+
else if (i = getIteratorFn(children), "function" === typeof i) for (i === children.entries && (didWarnAboutMaps || console.warn("Using Maps as children is not supported. Use an array of keyed ReactElements instead."), didWarnAboutMaps = !0), children = i.call(children), i = 0; !(nameSoFar = children.next()).done;) nameSoFar = nameSoFar.value, type = childKey + getElementKey(nameSoFar, i++), invokeCallback += mapIntoArray(nameSoFar, array, escapedPrefix, type, callback);
|
|
772
|
+
else if ("object" === type) {
|
|
773
|
+
if ("function" === typeof children.then) return mapIntoArray(resolveThenable(children), array, escapedPrefix, nameSoFar, callback);
|
|
774
|
+
array = String(children);
|
|
775
|
+
throw Error("Objects are not valid as a React child (found: " + ("[object Object]" === array ? "object with keys {" + Object.keys(children).join(", ") + "}" : array) + "). If you meant to render a collection of children, use an array instead.");
|
|
776
|
+
}
|
|
777
|
+
return invokeCallback;
|
|
778
|
+
}
|
|
779
|
+
function mapChildren(children, func, context) {
|
|
780
|
+
if (null == children) return children;
|
|
781
|
+
var result = [], count = 0;
|
|
782
|
+
mapIntoArray(children, result, "", "", function(child) {
|
|
783
|
+
return func.call(context, child, count++);
|
|
784
|
+
});
|
|
785
|
+
return result;
|
|
786
|
+
}
|
|
787
|
+
function lazyInitializer(payload) {
|
|
788
|
+
if (-1 === payload._status) {
|
|
789
|
+
var ioInfo = payload._ioInfo;
|
|
790
|
+
null != ioInfo && (ioInfo.start = ioInfo.end = performance.now());
|
|
791
|
+
ioInfo = payload._result;
|
|
792
|
+
var thenable = ioInfo();
|
|
793
|
+
thenable.then(function(moduleObject) {
|
|
794
|
+
if (0 === payload._status || -1 === payload._status) {
|
|
795
|
+
payload._status = 1;
|
|
796
|
+
payload._result = moduleObject;
|
|
797
|
+
var _ioInfo = payload._ioInfo;
|
|
798
|
+
null != _ioInfo && (_ioInfo.end = performance.now());
|
|
799
|
+
void 0 === thenable.status && (thenable.status = "fulfilled", thenable.value = moduleObject);
|
|
800
|
+
}
|
|
801
|
+
}, function(error) {
|
|
802
|
+
if (0 === payload._status || -1 === payload._status) {
|
|
803
|
+
payload._status = 2;
|
|
804
|
+
payload._result = error;
|
|
805
|
+
var _ioInfo2 = payload._ioInfo;
|
|
806
|
+
null != _ioInfo2 && (_ioInfo2.end = performance.now());
|
|
807
|
+
void 0 === thenable.status && (thenable.status = "rejected", thenable.reason = error);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
ioInfo = payload._ioInfo;
|
|
811
|
+
if (null != ioInfo) {
|
|
812
|
+
ioInfo.value = thenable;
|
|
813
|
+
var displayName = thenable.displayName;
|
|
814
|
+
"string" === typeof displayName && (ioInfo.name = displayName);
|
|
815
|
+
}
|
|
816
|
+
-1 === payload._status && (payload._status = 0, payload._result = thenable);
|
|
817
|
+
}
|
|
818
|
+
if (1 === payload._status) return ioInfo = payload._result, void 0 === ioInfo && console.error("lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))\n\nDid you accidentally put curly braces around the import?", ioInfo), "default" in ioInfo || console.error("lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))", ioInfo), ioInfo.default;
|
|
819
|
+
throw payload._result;
|
|
820
|
+
}
|
|
821
|
+
function resolveDispatcher() {
|
|
822
|
+
var dispatcher = ReactSharedInternals.H;
|
|
823
|
+
null === dispatcher && console.error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.");
|
|
824
|
+
return dispatcher;
|
|
825
|
+
}
|
|
826
|
+
function releaseAsyncTransition() {
|
|
827
|
+
ReactSharedInternals.asyncTransitions--;
|
|
828
|
+
}
|
|
829
|
+
function enqueueTask(task) {
|
|
830
|
+
if (null === enqueueTaskImpl) try {
|
|
831
|
+
var requireString = ("require" + Math.random()).slice(0, 7);
|
|
832
|
+
enqueueTaskImpl = (module && module[requireString]).call(module, "timers").setImmediate;
|
|
833
|
+
} catch (_err) {
|
|
834
|
+
enqueueTaskImpl = function(callback) {
|
|
835
|
+
!1 === didWarnAboutMessageChannel && (didWarnAboutMessageChannel = !0, "undefined" === typeof MessageChannel && console.error("This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."));
|
|
836
|
+
var channel = new MessageChannel();
|
|
837
|
+
channel.port1.onmessage = callback;
|
|
838
|
+
channel.port2.postMessage(void 0);
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
return enqueueTaskImpl(task);
|
|
842
|
+
}
|
|
843
|
+
function aggregateErrors(errors) {
|
|
844
|
+
return 1 < errors.length && "function" === typeof AggregateError ? new AggregateError(errors) : errors[0];
|
|
845
|
+
}
|
|
846
|
+
function popActScope(prevActQueue, prevActScopeDepth) {
|
|
847
|
+
prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
|
|
848
|
+
actScopeDepth = prevActScopeDepth;
|
|
849
|
+
}
|
|
850
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve, reject) {
|
|
851
|
+
var queue = ReactSharedInternals.actQueue;
|
|
852
|
+
if (null !== queue) if (0 !== queue.length) try {
|
|
853
|
+
flushActQueue(queue);
|
|
854
|
+
enqueueTask(function() {
|
|
855
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve, reject);
|
|
856
|
+
});
|
|
857
|
+
return;
|
|
858
|
+
} catch (error) {
|
|
859
|
+
ReactSharedInternals.thrownErrors.push(error);
|
|
860
|
+
}
|
|
861
|
+
else ReactSharedInternals.actQueue = null;
|
|
862
|
+
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve(returnValue);
|
|
863
|
+
}
|
|
864
|
+
function flushActQueue(queue) {
|
|
865
|
+
if (!isFlushing) {
|
|
866
|
+
isFlushing = !0;
|
|
867
|
+
var i = 0;
|
|
868
|
+
try {
|
|
869
|
+
for (; i < queue.length; i++) {
|
|
870
|
+
var callback = queue[i];
|
|
871
|
+
do {
|
|
872
|
+
ReactSharedInternals.didUsePromise = !1;
|
|
873
|
+
var continuation = callback(!1);
|
|
874
|
+
if (null !== continuation) {
|
|
875
|
+
if (ReactSharedInternals.didUsePromise) {
|
|
876
|
+
queue[i] = callback;
|
|
877
|
+
queue.splice(0, i);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
callback = continuation;
|
|
881
|
+
} else break;
|
|
882
|
+
} while (1);
|
|
883
|
+
}
|
|
884
|
+
queue.length = 0;
|
|
885
|
+
} catch (error) {
|
|
886
|
+
queue.splice(0, i + 1), ReactSharedInternals.thrownErrors.push(error);
|
|
887
|
+
} finally {
|
|
888
|
+
isFlushing = !1;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
893
|
+
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), MAYBE_ITERATOR_SYMBOL = Symbol.iterator, didWarnStateUpdateForUnmountedComponent = {}, ReactNoopUpdateQueue = {
|
|
894
|
+
isMounted: function() {
|
|
895
|
+
return !1;
|
|
896
|
+
},
|
|
897
|
+
enqueueForceUpdate: function(publicInstance) {
|
|
898
|
+
warnNoop(publicInstance, "forceUpdate");
|
|
899
|
+
},
|
|
900
|
+
enqueueReplaceState: function(publicInstance) {
|
|
901
|
+
warnNoop(publicInstance, "replaceState");
|
|
902
|
+
},
|
|
903
|
+
enqueueSetState: function(publicInstance) {
|
|
904
|
+
warnNoop(publicInstance, "setState");
|
|
905
|
+
}
|
|
906
|
+
}, assign = Object.assign, emptyObject = {};
|
|
907
|
+
Object.freeze(emptyObject);
|
|
908
|
+
Component.prototype.isReactComponent = {};
|
|
909
|
+
Component.prototype.setState = function(partialState, callback) {
|
|
910
|
+
if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState) throw Error("takes an object of state variables to update or a function which returns an object of state variables.");
|
|
911
|
+
this.updater.enqueueSetState(this, partialState, callback, "setState");
|
|
912
|
+
};
|
|
913
|
+
Component.prototype.forceUpdate = function(callback) {
|
|
914
|
+
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
|
|
915
|
+
};
|
|
916
|
+
var deprecatedAPIs = {
|
|
917
|
+
isMounted: ["isMounted", "Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],
|
|
918
|
+
replaceState: ["replaceState", "Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]
|
|
919
|
+
};
|
|
920
|
+
for (fnName in deprecatedAPIs) deprecatedAPIs.hasOwnProperty(fnName) && defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
|
|
921
|
+
ComponentDummy.prototype = Component.prototype;
|
|
922
|
+
deprecatedAPIs = PureComponent.prototype = new ComponentDummy();
|
|
923
|
+
deprecatedAPIs.constructor = PureComponent;
|
|
924
|
+
assign(deprecatedAPIs, Component.prototype);
|
|
925
|
+
deprecatedAPIs.isPureReactComponent = !0;
|
|
926
|
+
var isArrayImpl = Array.isArray, REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = {
|
|
927
|
+
H: null,
|
|
928
|
+
A: null,
|
|
929
|
+
T: null,
|
|
930
|
+
S: null,
|
|
931
|
+
actQueue: null,
|
|
932
|
+
asyncTransitions: 0,
|
|
933
|
+
isBatchingLegacy: !1,
|
|
934
|
+
didScheduleLegacyUpdate: !1,
|
|
935
|
+
didUsePromise: !1,
|
|
936
|
+
thrownErrors: [],
|
|
937
|
+
getCurrentStack: null,
|
|
938
|
+
recentlyCreatedOwnerStacks: 0
|
|
939
|
+
}, hasOwnProperty = Object.prototype.hasOwnProperty, createTask = console.createTask ? console.createTask : function() {
|
|
940
|
+
return null;
|
|
941
|
+
};
|
|
942
|
+
deprecatedAPIs = { react_stack_bottom_frame: function(callStackForError) {
|
|
943
|
+
return callStackForError();
|
|
944
|
+
} };
|
|
945
|
+
var specialPropKeyWarningShown, didWarnAboutOldJSXRuntime;
|
|
946
|
+
var didWarnAboutElementRef = {};
|
|
947
|
+
var unknownOwnerDebugStack = deprecatedAPIs.react_stack_bottom_frame.bind(deprecatedAPIs, UnknownOwner)();
|
|
948
|
+
var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
|
|
949
|
+
var didWarnAboutMaps = !1, userProvidedKeyEscapeRegex = /\/+/g, reportGlobalError = "function" === typeof reportError ? reportError : function(error) {
|
|
950
|
+
if ("object" === typeof window && "function" === typeof window.ErrorEvent) {
|
|
951
|
+
var event = new window.ErrorEvent("error", {
|
|
952
|
+
bubbles: !0,
|
|
953
|
+
cancelable: !0,
|
|
954
|
+
message: "object" === typeof error && null !== error && "string" === typeof error.message ? String(error.message) : String(error),
|
|
955
|
+
error
|
|
956
|
+
});
|
|
957
|
+
if (!window.dispatchEvent(event)) return;
|
|
958
|
+
} else if ("object" === typeof process && "function" === typeof process.emit) {
|
|
959
|
+
process.emit("uncaughtException", error);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
console.error(error);
|
|
963
|
+
}, didWarnAboutMessageChannel = !1, enqueueTaskImpl = null, actScopeDepth = 0, didWarnNoAwaitAct = !1, isFlushing = !1, queueSeveralMicrotasks = "function" === typeof queueMicrotask ? function(callback) {
|
|
964
|
+
queueMicrotask(function() {
|
|
965
|
+
return queueMicrotask(callback);
|
|
966
|
+
});
|
|
967
|
+
} : enqueueTask;
|
|
968
|
+
deprecatedAPIs = Object.freeze({
|
|
969
|
+
__proto__: null,
|
|
970
|
+
c: function(size) {
|
|
971
|
+
return resolveDispatcher().useMemoCache(size);
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
var fnName = {
|
|
975
|
+
map: mapChildren,
|
|
976
|
+
forEach: function(children, forEachFunc, forEachContext) {
|
|
977
|
+
mapChildren(children, function() {
|
|
978
|
+
forEachFunc.apply(this, arguments);
|
|
979
|
+
}, forEachContext);
|
|
980
|
+
},
|
|
981
|
+
count: function(children) {
|
|
982
|
+
var n = 0;
|
|
983
|
+
mapChildren(children, function() {
|
|
984
|
+
n++;
|
|
985
|
+
});
|
|
986
|
+
return n;
|
|
987
|
+
},
|
|
988
|
+
toArray: function(children) {
|
|
989
|
+
return mapChildren(children, function(child) {
|
|
990
|
+
return child;
|
|
991
|
+
}) || [];
|
|
992
|
+
},
|
|
993
|
+
only: function(children) {
|
|
994
|
+
if (!isValidElement$1(children)) throw Error("React.Children.only expected to receive a single React element child.");
|
|
995
|
+
return children;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
exports.Activity = REACT_ACTIVITY_TYPE;
|
|
999
|
+
exports.Children = fnName;
|
|
1000
|
+
exports.Component = Component;
|
|
1001
|
+
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
1002
|
+
exports.Profiler = REACT_PROFILER_TYPE;
|
|
1003
|
+
exports.PureComponent = PureComponent;
|
|
1004
|
+
exports.StrictMode = REACT_STRICT_MODE_TYPE;
|
|
1005
|
+
exports.Suspense = REACT_SUSPENSE_TYPE;
|
|
1006
|
+
exports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = ReactSharedInternals;
|
|
1007
|
+
exports.__COMPILER_RUNTIME = deprecatedAPIs;
|
|
1008
|
+
exports.act = function(callback) {
|
|
1009
|
+
var prevActQueue = ReactSharedInternals.actQueue, prevActScopeDepth = actScopeDepth;
|
|
1010
|
+
actScopeDepth++;
|
|
1011
|
+
var queue = ReactSharedInternals.actQueue = null !== prevActQueue ? prevActQueue : [], didAwaitActCall = !1;
|
|
1012
|
+
try {
|
|
1013
|
+
var result = callback();
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
ReactSharedInternals.thrownErrors.push(error);
|
|
1016
|
+
}
|
|
1017
|
+
if (0 < ReactSharedInternals.thrownErrors.length) throw popActScope(prevActQueue, prevActScopeDepth), callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
1018
|
+
if (null !== result && "object" === typeof result && "function" === typeof result.then) {
|
|
1019
|
+
var thenable = result;
|
|
1020
|
+
queueSeveralMicrotasks(function() {
|
|
1021
|
+
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
|
|
1022
|
+
});
|
|
1023
|
+
return { then: function(resolve, reject) {
|
|
1024
|
+
didAwaitActCall = !0;
|
|
1025
|
+
thenable.then(function(returnValue) {
|
|
1026
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1027
|
+
if (0 === prevActScopeDepth) {
|
|
1028
|
+
try {
|
|
1029
|
+
flushActQueue(queue), enqueueTask(function() {
|
|
1030
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve, reject);
|
|
1031
|
+
});
|
|
1032
|
+
} catch (error$0) {
|
|
1033
|
+
ReactSharedInternals.thrownErrors.push(error$0);
|
|
1034
|
+
}
|
|
1035
|
+
if (0 < ReactSharedInternals.thrownErrors.length) {
|
|
1036
|
+
var _thrownError = aggregateErrors(ReactSharedInternals.thrownErrors);
|
|
1037
|
+
ReactSharedInternals.thrownErrors.length = 0;
|
|
1038
|
+
reject(_thrownError);
|
|
1039
|
+
}
|
|
1040
|
+
} else resolve(returnValue);
|
|
1041
|
+
}, function(error) {
|
|
1042
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1043
|
+
0 < ReactSharedInternals.thrownErrors.length ? (error = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error)) : reject(error);
|
|
1044
|
+
});
|
|
1045
|
+
} };
|
|
1046
|
+
}
|
|
1047
|
+
var returnValue$jscomp$0 = result;
|
|
1048
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1049
|
+
0 === prevActScopeDepth && (flushActQueue(queue), 0 !== queue.length && queueSeveralMicrotasks(function() {
|
|
1050
|
+
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"));
|
|
1051
|
+
}), ReactSharedInternals.actQueue = null);
|
|
1052
|
+
if (0 < ReactSharedInternals.thrownErrors.length) throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
1053
|
+
return { then: function(resolve, reject) {
|
|
1054
|
+
didAwaitActCall = !0;
|
|
1055
|
+
0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
|
|
1056
|
+
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve, reject);
|
|
1057
|
+
})) : resolve(returnValue$jscomp$0);
|
|
1058
|
+
} };
|
|
1059
|
+
};
|
|
1060
|
+
exports.cache = function(fn) {
|
|
1061
|
+
return function() {
|
|
1062
|
+
return fn.apply(null, arguments);
|
|
1063
|
+
};
|
|
1064
|
+
};
|
|
1065
|
+
exports.cacheSignal = function() {
|
|
1066
|
+
return null;
|
|
1067
|
+
};
|
|
1068
|
+
exports.captureOwnerStack = function() {
|
|
1069
|
+
var getCurrentStack = ReactSharedInternals.getCurrentStack;
|
|
1070
|
+
return null === getCurrentStack ? null : getCurrentStack();
|
|
1071
|
+
};
|
|
1072
|
+
exports.cloneElement = function(element, config, children) {
|
|
1073
|
+
if (null === element || void 0 === element) throw Error("The argument must be a React element, but you passed " + element + ".");
|
|
1074
|
+
var props = assign({}, element.props), key = element.key, owner = element._owner;
|
|
1075
|
+
if (null != config) {
|
|
1076
|
+
var JSCompiler_inline_result;
|
|
1077
|
+
a: {
|
|
1078
|
+
if (hasOwnProperty.call(config, "ref") && (JSCompiler_inline_result = Object.getOwnPropertyDescriptor(config, "ref").get) && JSCompiler_inline_result.isReactWarning) {
|
|
1079
|
+
JSCompiler_inline_result = !1;
|
|
1080
|
+
break a;
|
|
1081
|
+
}
|
|
1082
|
+
JSCompiler_inline_result = void 0 !== config.ref;
|
|
1083
|
+
}
|
|
1084
|
+
JSCompiler_inline_result && (owner = getOwner());
|
|
1085
|
+
hasValidKey(config) && (checkKeyStringCoercion(config.key), key = "" + config.key);
|
|
1086
|
+
for (propName in config) !hasOwnProperty.call(config, propName) || "key" === propName || "__self" === propName || "__source" === propName || "ref" === propName && void 0 === config.ref || (props[propName] = config[propName]);
|
|
1087
|
+
}
|
|
1088
|
+
var propName = arguments.length - 2;
|
|
1089
|
+
if (1 === propName) props.children = children;
|
|
1090
|
+
else if (1 < propName) {
|
|
1091
|
+
JSCompiler_inline_result = Array(propName);
|
|
1092
|
+
for (var i = 0; i < propName; i++) JSCompiler_inline_result[i] = arguments[i + 2];
|
|
1093
|
+
props.children = JSCompiler_inline_result;
|
|
1094
|
+
}
|
|
1095
|
+
props = ReactElement(element.type, key, props, owner, element._debugStack, element._debugTask);
|
|
1096
|
+
for (key = 2; key < arguments.length; key++) validateChildKeys(arguments[key]);
|
|
1097
|
+
return props;
|
|
1098
|
+
};
|
|
1099
|
+
exports.createContext = function(defaultValue) {
|
|
1100
|
+
defaultValue = {
|
|
1101
|
+
$$typeof: REACT_CONTEXT_TYPE,
|
|
1102
|
+
_currentValue: defaultValue,
|
|
1103
|
+
_currentValue2: defaultValue,
|
|
1104
|
+
_threadCount: 0,
|
|
1105
|
+
Provider: null,
|
|
1106
|
+
Consumer: null
|
|
1107
|
+
};
|
|
1108
|
+
defaultValue.Provider = defaultValue;
|
|
1109
|
+
defaultValue.Consumer = {
|
|
1110
|
+
$$typeof: REACT_CONSUMER_TYPE,
|
|
1111
|
+
_context: defaultValue
|
|
1112
|
+
};
|
|
1113
|
+
defaultValue._currentRenderer = null;
|
|
1114
|
+
defaultValue._currentRenderer2 = null;
|
|
1115
|
+
return defaultValue;
|
|
1116
|
+
};
|
|
1117
|
+
exports.createElement = function(type, config, children) {
|
|
1118
|
+
for (var i = 2; i < arguments.length; i++) validateChildKeys(arguments[i]);
|
|
1119
|
+
i = {};
|
|
1120
|
+
var key = null;
|
|
1121
|
+
if (null != config) for (propName in didWarnAboutOldJSXRuntime || !("__self" in config) || "key" in config || (didWarnAboutOldJSXRuntime = !0, console.warn("Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform")), hasValidKey(config) && (checkKeyStringCoercion(config.key), key = "" + config.key), config) hasOwnProperty.call(config, propName) && "key" !== propName && "__self" !== propName && "__source" !== propName && (i[propName] = config[propName]);
|
|
1122
|
+
var childrenLength = arguments.length - 2;
|
|
1123
|
+
if (1 === childrenLength) i.children = children;
|
|
1124
|
+
else if (1 < childrenLength) {
|
|
1125
|
+
for (var childArray = Array(childrenLength), _i = 0; _i < childrenLength; _i++) childArray[_i] = arguments[_i + 2];
|
|
1126
|
+
Object.freeze && Object.freeze(childArray);
|
|
1127
|
+
i.children = childArray;
|
|
1128
|
+
}
|
|
1129
|
+
if (type && type.defaultProps) for (propName in childrenLength = type.defaultProps, childrenLength) void 0 === i[propName] && (i[propName] = childrenLength[propName]);
|
|
1130
|
+
key && defineKeyPropWarningGetter(i, "function" === typeof type ? type.displayName || type.name || "Unknown" : type);
|
|
1131
|
+
var propName = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
|
|
1132
|
+
return ReactElement(type, key, i, getOwner(), propName ? Error("react-stack-top-frame") : unknownOwnerDebugStack, propName ? createTask(getTaskName(type)) : unknownOwnerDebugTask);
|
|
1133
|
+
};
|
|
1134
|
+
exports.createRef = function() {
|
|
1135
|
+
var refObject = { current: null };
|
|
1136
|
+
Object.seal(refObject);
|
|
1137
|
+
return refObject;
|
|
1138
|
+
};
|
|
1139
|
+
exports.forwardRef = function(render) {
|
|
1140
|
+
null != render && render.$$typeof === REACT_MEMO_TYPE ? console.error("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...)).") : "function" !== typeof render ? console.error("forwardRef requires a render function but was given %s.", null === render ? "null" : typeof render) : 0 !== render.length && 2 !== render.length && console.error("forwardRef render functions accept exactly two parameters: props and ref. %s", 1 === render.length ? "Did you forget to use the ref parameter?" : "Any additional parameter will be undefined.");
|
|
1141
|
+
null != render && null != render.defaultProps && console.error("forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?");
|
|
1142
|
+
var elementType = {
|
|
1143
|
+
$$typeof: REACT_FORWARD_REF_TYPE,
|
|
1144
|
+
render
|
|
1145
|
+
}, ownName;
|
|
1146
|
+
Object.defineProperty(elementType, "displayName", {
|
|
1147
|
+
enumerable: !1,
|
|
1148
|
+
configurable: !0,
|
|
1149
|
+
get: function() {
|
|
1150
|
+
return ownName;
|
|
1151
|
+
},
|
|
1152
|
+
set: function(name) {
|
|
1153
|
+
ownName = name;
|
|
1154
|
+
render.name || render.displayName || (Object.defineProperty(render, "name", { value: name }), render.displayName = name);
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
return elementType;
|
|
1158
|
+
};
|
|
1159
|
+
exports.isValidElement = isValidElement$1;
|
|
1160
|
+
exports.lazy = function(ctor) {
|
|
1161
|
+
ctor = {
|
|
1162
|
+
_status: -1,
|
|
1163
|
+
_result: ctor
|
|
1164
|
+
};
|
|
1165
|
+
var lazyType = {
|
|
1166
|
+
$$typeof: REACT_LAZY_TYPE,
|
|
1167
|
+
_payload: ctor,
|
|
1168
|
+
_init: lazyInitializer
|
|
1169
|
+
}, ioInfo = {
|
|
1170
|
+
name: "lazy",
|
|
1171
|
+
start: -1,
|
|
1172
|
+
end: -1,
|
|
1173
|
+
value: null,
|
|
1174
|
+
owner: null,
|
|
1175
|
+
debugStack: Error("react-stack-top-frame"),
|
|
1176
|
+
debugTask: console.createTask ? console.createTask("lazy()") : null
|
|
1177
|
+
};
|
|
1178
|
+
ctor._ioInfo = ioInfo;
|
|
1179
|
+
lazyType._debugInfo = [{ awaited: ioInfo }];
|
|
1180
|
+
return lazyType;
|
|
1181
|
+
};
|
|
1182
|
+
exports.memo = function(type, compare) {
|
|
1183
|
+
type ?? console.error("memo: The first argument must be a component. Instead received: %s", null === type ? "null" : typeof type);
|
|
1184
|
+
compare = {
|
|
1185
|
+
$$typeof: REACT_MEMO_TYPE,
|
|
1186
|
+
type,
|
|
1187
|
+
compare: void 0 === compare ? null : compare
|
|
1188
|
+
};
|
|
1189
|
+
var ownName;
|
|
1190
|
+
Object.defineProperty(compare, "displayName", {
|
|
1191
|
+
enumerable: !1,
|
|
1192
|
+
configurable: !0,
|
|
1193
|
+
get: function() {
|
|
1194
|
+
return ownName;
|
|
1195
|
+
},
|
|
1196
|
+
set: function(name) {
|
|
1197
|
+
ownName = name;
|
|
1198
|
+
type.name || type.displayName || (Object.defineProperty(type, "name", { value: name }), type.displayName = name);
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
return compare;
|
|
1202
|
+
};
|
|
1203
|
+
exports.startTransition = function(scope) {
|
|
1204
|
+
var prevTransition = ReactSharedInternals.T, currentTransition = {};
|
|
1205
|
+
currentTransition._updatedFibers = /* @__PURE__ */ new Set();
|
|
1206
|
+
ReactSharedInternals.T = currentTransition;
|
|
1207
|
+
try {
|
|
1208
|
+
var returnValue = scope(), onStartTransitionFinish = ReactSharedInternals.S;
|
|
1209
|
+
null !== onStartTransitionFinish && onStartTransitionFinish(currentTransition, returnValue);
|
|
1210
|
+
"object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && (ReactSharedInternals.asyncTransitions++, returnValue.then(releaseAsyncTransition, releaseAsyncTransition), returnValue.then(noop, reportGlobalError));
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
reportGlobalError(error);
|
|
1213
|
+
} finally {
|
|
1214
|
+
null === prevTransition && currentTransition._updatedFibers && (scope = currentTransition._updatedFibers.size, currentTransition._updatedFibers.clear(), 10 < scope && console.warn("Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table.")), null !== prevTransition && null !== currentTransition.types && (null !== prevTransition.types && prevTransition.types !== currentTransition.types && console.error("We expected inner Transitions to have transferred the outer types set and that you cannot add to the outer Transition while inside the inner.This is a bug in React."), prevTransition.types = currentTransition.types), ReactSharedInternals.T = prevTransition;
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
exports.unstable_useCacheRefresh = function() {
|
|
1218
|
+
return resolveDispatcher().useCacheRefresh();
|
|
1219
|
+
};
|
|
1220
|
+
exports.use = function(usable) {
|
|
1221
|
+
return resolveDispatcher().use(usable);
|
|
1222
|
+
};
|
|
1223
|
+
exports.useActionState = function(action, initialState, permalink) {
|
|
1224
|
+
return resolveDispatcher().useActionState(action, initialState, permalink);
|
|
1225
|
+
};
|
|
1226
|
+
exports.useCallback = function(callback, deps) {
|
|
1227
|
+
return resolveDispatcher().useCallback(callback, deps);
|
|
1228
|
+
};
|
|
1229
|
+
exports.useContext = function(Context) {
|
|
1230
|
+
var dispatcher = resolveDispatcher();
|
|
1231
|
+
Context.$$typeof === REACT_CONSUMER_TYPE && console.error("Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?");
|
|
1232
|
+
return dispatcher.useContext(Context);
|
|
1233
|
+
};
|
|
1234
|
+
exports.useDebugValue = function(value, formatterFn) {
|
|
1235
|
+
return resolveDispatcher().useDebugValue(value, formatterFn);
|
|
1236
|
+
};
|
|
1237
|
+
exports.useDeferredValue = function(value, initialValue) {
|
|
1238
|
+
return resolveDispatcher().useDeferredValue(value, initialValue);
|
|
1239
|
+
};
|
|
1240
|
+
exports.useEffect = function(create, deps) {
|
|
1241
|
+
create ?? console.warn("React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?");
|
|
1242
|
+
return resolveDispatcher().useEffect(create, deps);
|
|
1243
|
+
};
|
|
1244
|
+
exports.useEffectEvent = function(callback) {
|
|
1245
|
+
return resolveDispatcher().useEffectEvent(callback);
|
|
1246
|
+
};
|
|
1247
|
+
exports.useId = function() {
|
|
1248
|
+
return resolveDispatcher().useId();
|
|
1249
|
+
};
|
|
1250
|
+
exports.useImperativeHandle = function(ref, create, deps) {
|
|
1251
|
+
return resolveDispatcher().useImperativeHandle(ref, create, deps);
|
|
1252
|
+
};
|
|
1253
|
+
exports.useInsertionEffect = function(create, deps) {
|
|
1254
|
+
create ?? console.warn("React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?");
|
|
1255
|
+
return resolveDispatcher().useInsertionEffect(create, deps);
|
|
1256
|
+
};
|
|
1257
|
+
exports.useLayoutEffect = function(create, deps) {
|
|
1258
|
+
create ?? console.warn("React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?");
|
|
1259
|
+
return resolveDispatcher().useLayoutEffect(create, deps);
|
|
1260
|
+
};
|
|
1261
|
+
exports.useMemo = function(create, deps) {
|
|
1262
|
+
return resolveDispatcher().useMemo(create, deps);
|
|
1263
|
+
};
|
|
1264
|
+
exports.useOptimistic = function(passthrough, reducer) {
|
|
1265
|
+
return resolveDispatcher().useOptimistic(passthrough, reducer);
|
|
1266
|
+
};
|
|
1267
|
+
exports.useReducer = function(reducer, initialArg, init) {
|
|
1268
|
+
return resolveDispatcher().useReducer(reducer, initialArg, init);
|
|
1269
|
+
};
|
|
1270
|
+
exports.useRef = function(initialValue) {
|
|
1271
|
+
return resolveDispatcher().useRef(initialValue);
|
|
1272
|
+
};
|
|
1273
|
+
exports.useState = function(initialState) {
|
|
1274
|
+
return resolveDispatcher().useState(initialState);
|
|
1275
|
+
};
|
|
1276
|
+
exports.useSyncExternalStore = function(subscribe, getSnapshot, getServerSnapshot) {
|
|
1277
|
+
return resolveDispatcher().useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1278
|
+
};
|
|
1279
|
+
exports.useTransition = function() {
|
|
1280
|
+
return resolveDispatcher().useTransition();
|
|
1281
|
+
};
|
|
1282
|
+
exports.version = "19.2.4";
|
|
1283
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
1284
|
+
})();
|
|
1285
|
+
}));
|
|
1286
|
+
|
|
1287
|
+
//#endregion
|
|
1288
|
+
//#region ../../../node_modules/.pnpm/react@19.2.4/node_modules/react/index.js
|
|
1289
|
+
var require_react = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1290
|
+
if (process.env.NODE_ENV === "production") module.exports = require_react_production();
|
|
1291
|
+
else module.exports = require_react_development();
|
|
1292
|
+
}));
|
|
1293
|
+
|
|
1294
|
+
//#endregion
|
|
1295
|
+
//#region src/commands/generate/utils/intl.ts
|
|
1296
|
+
var import_react = require_react();
|
|
1297
|
+
function isFormattedMessageElement(node) {
|
|
1298
|
+
return (0, import_react.isValidElement)(node) && node.type === FormattedMessage;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Generates a <FormattedMessage> JSX element or an intl.formatMessage call string supporting <FormattedMessage> config objects.
|
|
1302
|
+
*
|
|
1303
|
+
* either config or defaultMessage is required
|
|
1304
|
+
*/
|
|
1305
|
+
const generateFormattedMessage = (options) => {
|
|
1306
|
+
let id = options.id;
|
|
1307
|
+
let defaultMessage = "";
|
|
1308
|
+
if ("defaultMessage" in options) defaultMessage = options.defaultMessage;
|
|
1309
|
+
if (isFormattedMessageElement(options.config)) {
|
|
1310
|
+
if (!options.config.props.id) throw new Error("FormattedMessage requires an id");
|
|
1311
|
+
id = options.config.props.id;
|
|
1312
|
+
if (!options.config.props.defaultMessage) throw new Error("FormattedMessage requires an defaultMessage");
|
|
1313
|
+
if (typeof options.config.props.defaultMessage !== "string") throw new Error("FormattedMessage requires a string defaultMessage");
|
|
1314
|
+
defaultMessage = options.config.props.defaultMessage;
|
|
1315
|
+
} else if (typeof options.config === "string") defaultMessage = options.config;
|
|
1316
|
+
if (options.type === "jsx") return `<FormattedMessage id=${JSON.stringify(id)} defaultMessage=${JSON.stringify(defaultMessage)} />`;
|
|
1317
|
+
else return `intl.formatMessage({ id: ${JSON.stringify(id)}, defaultMessage: ${JSON.stringify(defaultMessage)} })`;
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
//#endregion
|
|
1321
|
+
//#region src/commands/generate/utils/isFieldOptional.ts
|
|
1322
|
+
const isFieldOptional = ({ config, gqlIntrospection, gqlType }) => {
|
|
1323
|
+
if (config.required !== void 0) return !config.required;
|
|
1324
|
+
if (config.readOnly) return true;
|
|
1325
|
+
const schemaEntity = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
1326
|
+
if (!schemaEntity) throw new Error(`didn't find entity ${gqlType} in schema types`);
|
|
1327
|
+
if (schemaEntity.kind !== "OBJECT") throw new Error(`kind of ${gqlType} is not object, but should be.`);
|
|
1328
|
+
const fieldDef = schemaEntity.fields.find((field) => field.name === String(config.name));
|
|
1329
|
+
if (!fieldDef) return false;
|
|
1330
|
+
return fieldDef.type.kind !== "NON_NULL";
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
//#endregion
|
|
1334
|
+
//#region src/commands/generate/utils/findQueryType.ts
|
|
1335
|
+
function findQueryType(queryName, schema) {
|
|
1336
|
+
const queryType = schema.__schema.types.find((type) => type.name === schema.__schema.queryType.name);
|
|
1337
|
+
if (!queryType) throw new Error("Can't find Query type in gql schema");
|
|
1338
|
+
const ret = queryType.fields.find((field) => field.name === queryName);
|
|
1339
|
+
if (!ret) throw new Error(`Can't find query ${queryName} in gql schema`);
|
|
1340
|
+
return ret;
|
|
1341
|
+
}
|
|
1342
|
+
function findQueryTypeOrThrow(queryName, schema) {
|
|
1343
|
+
const ret = findQueryType(queryName, schema);
|
|
1344
|
+
if (!ret) throw new Error(`Can't find query ${queryName} in gql schema`);
|
|
1345
|
+
return ret;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
//#endregion
|
|
1349
|
+
//#region src/commands/generate/generateForm/formField/options.ts
|
|
1350
|
+
const buildAdornmentData = ({ adornmentData }) => {
|
|
1351
|
+
let adornmentString = "";
|
|
1352
|
+
let adornmentImport = {
|
|
1353
|
+
name: "",
|
|
1354
|
+
importPath: ""
|
|
1355
|
+
};
|
|
1356
|
+
if (typeof adornmentData === "string") return { adornmentString: adornmentData };
|
|
1357
|
+
if (typeof adornmentData.icon === "string") {
|
|
1358
|
+
adornmentString = `<${adornmentData.icon}Icon />`;
|
|
1359
|
+
adornmentImport = {
|
|
1360
|
+
name: `${adornmentData.icon} as ${adornmentData.icon}Icon`,
|
|
1361
|
+
importPath: "@comet/admin-icons"
|
|
1362
|
+
};
|
|
1363
|
+
} else if (typeof adornmentData.icon === "object") if (isGeneratorConfigImport(adornmentData.icon)) {
|
|
1364
|
+
adornmentString = `<${adornmentData.icon.name} />`;
|
|
1365
|
+
adornmentImport = convertConfigImport(adornmentData.icon);
|
|
1366
|
+
} else {
|
|
1367
|
+
const { name, ...iconProps } = adornmentData.icon;
|
|
1368
|
+
adornmentString = `<${name}Icon
|
|
1369
|
+
${Object.entries(iconProps).map(([key, value]) => `${key}="${value}"`).join("\n")}
|
|
1370
|
+
/>`;
|
|
1371
|
+
adornmentImport = {
|
|
1372
|
+
name: `${adornmentData.icon.name} as ${adornmentData.icon.name}Icon`,
|
|
1373
|
+
importPath: "@comet/admin-icons"
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
adornmentString,
|
|
1378
|
+
adornmentImport
|
|
1379
|
+
};
|
|
1380
|
+
};
|
|
1381
|
+
/**
|
|
1382
|
+
* Helper function that builds various options needed for generating form fields.
|
|
1383
|
+
*/
|
|
1384
|
+
function buildFormFieldOptions({ config, formConfig }) {
|
|
1385
|
+
const rootGqlType = formConfig.gqlType;
|
|
1386
|
+
const name = String(config.name);
|
|
1387
|
+
const formattedMessageRootId = rootGqlType[0].toLowerCase() + rootGqlType.substring(1);
|
|
1388
|
+
const fieldLabel = generateFormattedMessage({
|
|
1389
|
+
config: config.label,
|
|
1390
|
+
id: `${formattedMessageRootId}.${name}`,
|
|
1391
|
+
defaultMessage: camelCaseToHumanReadable(name),
|
|
1392
|
+
type: "jsx"
|
|
1393
|
+
});
|
|
1394
|
+
const imports = [];
|
|
1395
|
+
let startAdornment = { adornmentString: "" };
|
|
1396
|
+
let endAdornment = { adornmentString: "" };
|
|
1397
|
+
if ("startAdornment" in config && config.startAdornment) {
|
|
1398
|
+
startAdornment = buildAdornmentData({ adornmentData: config.startAdornment });
|
|
1399
|
+
if (startAdornment.adornmentImport) imports.push(startAdornment.adornmentImport);
|
|
1400
|
+
}
|
|
1401
|
+
if ("endAdornment" in config && config.endAdornment) {
|
|
1402
|
+
endAdornment = buildAdornmentData({ adornmentData: config.endAdornment });
|
|
1403
|
+
if (endAdornment.adornmentImport) imports.push(endAdornment.adornmentImport);
|
|
1404
|
+
}
|
|
1405
|
+
return {
|
|
1406
|
+
name,
|
|
1407
|
+
formattedMessageRootId,
|
|
1408
|
+
fieldLabel,
|
|
1409
|
+
startAdornment,
|
|
1410
|
+
endAdornment,
|
|
1411
|
+
imports
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
//#endregion
|
|
1416
|
+
//#region src/commands/generate/generateForm/asyncSelect/generateAsyncSelect.ts
|
|
1417
|
+
function gqlScalarToTypescriptType(scalarName) {
|
|
1418
|
+
if (scalarName === "String" || scalarName === "ID" || scalarName === "DateTime" || scalarName === "LocalDate" || scalarName === "Date") return "string";
|
|
1419
|
+
else if (scalarName === "Boolean") return "boolean";
|
|
1420
|
+
else if (scalarName === "Int" || scalarName === "Float") return "number";
|
|
1421
|
+
else if (scalarName === "JSONObject") return "unknown";
|
|
1422
|
+
else return "unknown";
|
|
1423
|
+
}
|
|
1424
|
+
function buildTypeInfo(arg, gqlIntrospection) {
|
|
1425
|
+
let typeKind = void 0;
|
|
1426
|
+
let typeClass = "unknown";
|
|
1427
|
+
let required = false;
|
|
1428
|
+
let type = arg.type;
|
|
1429
|
+
let inputType = void 0;
|
|
1430
|
+
if (type.kind === "NON_NULL") {
|
|
1431
|
+
required = true;
|
|
1432
|
+
type = type.ofType;
|
|
1433
|
+
}
|
|
1434
|
+
if (type.kind === "INPUT_OBJECT") {
|
|
1435
|
+
typeClass = type.name;
|
|
1436
|
+
typeKind = type.kind;
|
|
1437
|
+
inputType = gqlIntrospection.__schema.types.find((type$1) => type$1.name === typeClass);
|
|
1438
|
+
} else if (type.kind === "ENUM") {
|
|
1439
|
+
typeClass = type.name;
|
|
1440
|
+
typeKind = type.kind;
|
|
1441
|
+
} else if (type.kind === "SCALAR") {
|
|
1442
|
+
typeClass = type.name;
|
|
1443
|
+
typeKind = type.kind;
|
|
1444
|
+
} else throw new Error(`getTypeInfo: Resolving kind ${type.kind} currently not supported.`);
|
|
1445
|
+
return {
|
|
1446
|
+
required,
|
|
1447
|
+
typeKind,
|
|
1448
|
+
typeClass,
|
|
1449
|
+
inputType
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Helper that returns the introspection object type for a given form field config, supporting the special case for asyncSelectFilter
|
|
1454
|
+
*/
|
|
1455
|
+
function findIntrospectionObjectType({ config, gqlIntrospection, gqlType }) {
|
|
1456
|
+
const name = String(config.name);
|
|
1457
|
+
const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
1458
|
+
if (!introspectionObject) throw new Error(`didn't find object ${gqlType} in gql introspection`);
|
|
1459
|
+
function findIntrospectionField(introspectionObject$1, name$1) {
|
|
1460
|
+
const introspectionField = introspectionObject$1.fields.find((field) => field.name === name$1);
|
|
1461
|
+
if (!introspectionField) throw new Error(`didn't find field ${name$1} in gql introspection type ${gqlType}`);
|
|
1462
|
+
let introspectionFieldType = introspectionField.type.kind === "NON_NULL" ? introspectionField.type.ofType : introspectionField.type;
|
|
1463
|
+
const multiple = introspectionFieldType?.kind === "LIST";
|
|
1464
|
+
if (introspectionFieldType?.kind === "LIST") introspectionFieldType = introspectionFieldType.ofType.kind === "NON_NULL" ? introspectionFieldType.ofType.ofType : introspectionFieldType.ofType;
|
|
1465
|
+
if (introspectionFieldType.kind !== "OBJECT") throw new Error(`asyncSelect only supports OBJECT types`);
|
|
1466
|
+
const objectType = gqlIntrospection.__schema.types.find((t) => t.kind === "OBJECT" && t.name === introspectionFieldType.name);
|
|
1467
|
+
if (!objectType) throw new Error(`Object type ${introspectionFieldType.name} not found for field ${name$1}`);
|
|
1468
|
+
return {
|
|
1469
|
+
multiple,
|
|
1470
|
+
objectType
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
if (config.type === "asyncSelectFilter") return {
|
|
1474
|
+
multiple: false,
|
|
1475
|
+
objectType: config.loadValueQueryField.split(".").reduce((acc, fieldName) => {
|
|
1476
|
+
const introspectionField = findIntrospectionField(acc, fieldName);
|
|
1477
|
+
if (introspectionField.multiple) throw new Error(`asyncSelectFilter does not support list fields in loadValueQueryField`);
|
|
1478
|
+
return introspectionField.objectType;
|
|
1479
|
+
}, introspectionObject)
|
|
1480
|
+
};
|
|
1481
|
+
else return findIntrospectionField(introspectionObject, name);
|
|
1482
|
+
}
|
|
1483
|
+
function generateAsyncSelect({ gqlIntrospection, baseOutputFilename, config, formConfig, gqlType, namePrefix }) {
|
|
1484
|
+
const imports = [];
|
|
1485
|
+
const formProps = [];
|
|
1486
|
+
const { name, fieldLabel, startAdornment, imports: optionsImports } = buildFormFieldOptions({
|
|
1487
|
+
config,
|
|
1488
|
+
formConfig
|
|
1489
|
+
});
|
|
1490
|
+
imports.push(...optionsImports);
|
|
1491
|
+
const nameWithPrefix = `${namePrefix ? `${namePrefix}.` : ``}${name}`;
|
|
1492
|
+
const required = !isFieldOptional({
|
|
1493
|
+
config,
|
|
1494
|
+
gqlIntrospection,
|
|
1495
|
+
gqlType
|
|
1496
|
+
});
|
|
1497
|
+
const formValueConfig = {
|
|
1498
|
+
fieldName: name,
|
|
1499
|
+
destructFromFormValues: config.type == "asyncSelectFilter"
|
|
1500
|
+
};
|
|
1501
|
+
let finalFormConfig;
|
|
1502
|
+
let code = "";
|
|
1503
|
+
const { objectType, multiple } = findIntrospectionObjectType({
|
|
1504
|
+
config,
|
|
1505
|
+
gqlIntrospection,
|
|
1506
|
+
gqlType
|
|
1507
|
+
});
|
|
1508
|
+
let labelField = config.labelField;
|
|
1509
|
+
if (!labelField) labelField = objectType.fields.find((field) => {
|
|
1510
|
+
let type = field.type;
|
|
1511
|
+
if (type.kind == "NON_NULL") type = type.ofType;
|
|
1512
|
+
if ((field.name == "name" || field.name == "title") && type.kind == "SCALAR" && type.name == "String") return true;
|
|
1513
|
+
})?.name;
|
|
1514
|
+
if (!labelField) labelField = objectType.fields.find((field) => {
|
|
1515
|
+
let type = field.type;
|
|
1516
|
+
if (type.kind == "NON_NULL") type = type.ofType;
|
|
1517
|
+
if (field.type.kind == "SCALAR" && field.type.name == "String") return true;
|
|
1518
|
+
})?.name;
|
|
1519
|
+
const rootQuery = config.rootQuery;
|
|
1520
|
+
const queryName = `${rootQuery[0].toUpperCase() + rootQuery.substring(1)}Select`;
|
|
1521
|
+
const rootQueryType = findQueryTypeOrThrow(rootQuery, gqlIntrospection);
|
|
1522
|
+
let formFragmentFields;
|
|
1523
|
+
if (config.type == "asyncSelectFilter") formFragmentFields = [`${config.loadValueQueryField}.id`, `${config.loadValueQueryField}.${labelField}`];
|
|
1524
|
+
else formFragmentFields = [`${name}.id`, `${name}.${labelField}`];
|
|
1525
|
+
const rootQueryHasSearchArg = rootQueryType.args.find((arg) => arg.name === "search");
|
|
1526
|
+
const useAutocomplete = config.autocomplete ?? !!rootQueryHasSearchArg;
|
|
1527
|
+
if (useAutocomplete) {
|
|
1528
|
+
if (!rootQueryHasSearchArg) throw new Error(`Field ${String(config.name)}: Autocomplete is enabled but root query "${rootQuery}" has no search argument.`);
|
|
1529
|
+
if (rootQueryHasSearchArg.type.kind !== "SCALAR" || rootQueryHasSearchArg.type.name !== "String") throw new Error(`Field ${String(config.name)}: Autocomplete is enabled but root query "${rootQuery}" has search argument that is not a string.`);
|
|
1530
|
+
}
|
|
1531
|
+
const filterConfig = config.filter ? (() => {
|
|
1532
|
+
let filterField;
|
|
1533
|
+
let rootQueryArg = config.filter.rootQueryArg;
|
|
1534
|
+
let filterVar = "";
|
|
1535
|
+
if (config.filter.type === "field") {
|
|
1536
|
+
filterField = findFieldByName(config.filter.formFieldName, formConfig.fields);
|
|
1537
|
+
if (!filterField) throw new Error(`Field ${String(config.name)}: No field with name "${config.filter.formFieldName}" referenced as filter.formFieldName found in form-config.`);
|
|
1538
|
+
if (!isFormFieldConfig(filterField)) throw new Error(`Field ${String(config.name)}: Field with name "${config.filter.formFieldName}" referenced as filter.formFieldName is no FormField.`);
|
|
1539
|
+
filterVar = `values.${filterField.type === "asyncSelect" || filterField.type === "asyncSelectFilter" ? `${String(filterField.name)}?.id` : String(filterField.name)}`;
|
|
1540
|
+
if (!rootQueryArg) rootQueryArg = config.filter.formFieldName;
|
|
1541
|
+
} else if (config.filter.type === "formProp") {
|
|
1542
|
+
filterVar = config.filter.propName;
|
|
1543
|
+
if (!rootQueryArg) rootQueryArg = config.filter.propName;
|
|
1544
|
+
} else throw new Error("unsupported filter type");
|
|
1545
|
+
const rootArgForName = rootQueryType.args.find((arg) => arg.name === rootQueryArg);
|
|
1546
|
+
let filterType = rootArgForName ? buildTypeInfo(rootArgForName, gqlIntrospection) : void 0;
|
|
1547
|
+
let filterVarName = void 0;
|
|
1548
|
+
let filterVarValue = void 0;
|
|
1549
|
+
let filterVarType = "unknown";
|
|
1550
|
+
if (filterType) {
|
|
1551
|
+
filterVarName = rootQueryArg;
|
|
1552
|
+
filterVarValue = filterVar;
|
|
1553
|
+
if (filterType.typeKind === "INPUT_OBJECT" || filterType.typeKind === "ENUM") {
|
|
1554
|
+
filterVarType = `GQL${filterType.typeClass}`;
|
|
1555
|
+
imports.push({
|
|
1556
|
+
name: filterVarType,
|
|
1557
|
+
importPath: "@src/graphql.generated"
|
|
1558
|
+
});
|
|
1559
|
+
} else if (filterType.typeKind === "SCALAR") filterVarType = gqlScalarToTypescriptType(filterType.typeClass);
|
|
1560
|
+
} else {
|
|
1561
|
+
const rootArgFilter = rootQueryType.args.find((arg) => arg.name === "filter");
|
|
1562
|
+
filterType = rootArgFilter ? buildTypeInfo(rootArgFilter, gqlIntrospection) : void 0;
|
|
1563
|
+
if (filterType) {
|
|
1564
|
+
filterVarName = "filter";
|
|
1565
|
+
filterVarValue = `{ ${rootQueryArg}: { equal: ${filterVar} } }`;
|
|
1566
|
+
if (filterType.inputType?.kind !== "INPUT_OBJECT") throw new Error(`Field ${String(config.name)}: Type of filter is no object-type.`);
|
|
1567
|
+
const nestedFilterInput = filterType.inputType.inputFields.find((inputField) => inputField.name === rootQueryArg);
|
|
1568
|
+
if (!nestedFilterInput) throw new Error(`Field ${String(config.name)}: Field filter.${rootQueryArg} does not exist`);
|
|
1569
|
+
const gqlFilterInputType = buildTypeInfo(nestedFilterInput, gqlIntrospection);
|
|
1570
|
+
if (!gqlFilterInputType?.inputType || gqlFilterInputType.inputType.kind !== "INPUT_OBJECT") throw new Error(`Field ${String(config.name)}: Type of filter.${rootQueryArg} is no object-type, but needs to be e.g. StringFilter-type.`);
|
|
1571
|
+
const gqlFilterEqualInputType = gqlFilterInputType.inputType.inputFields.find((inputField) => inputField.name === "equal");
|
|
1572
|
+
if (!gqlFilterEqualInputType) throw new Error(`Field ${String(config.name)}: Field filter.${rootQueryArg}.equal does not exist`);
|
|
1573
|
+
const equalFieldType = buildTypeInfo(gqlFilterEqualInputType, gqlIntrospection);
|
|
1574
|
+
if (!equalFieldType) throw new Error(`Field ${String(config.name)}: Field filter.${rootQueryArg}.equal does not exist but is required for filtering.`);
|
|
1575
|
+
if (equalFieldType.typeKind === "INPUT_OBJECT" || equalFieldType.typeKind === "ENUM") {
|
|
1576
|
+
filterVarType = `GQL${equalFieldType.typeClass}`;
|
|
1577
|
+
imports.push({
|
|
1578
|
+
name: filterVarType,
|
|
1579
|
+
importPath: "@src/graphql.generated"
|
|
1580
|
+
});
|
|
1581
|
+
} else if (equalFieldType.typeKind === "SCALAR") filterVarType = gqlScalarToTypescriptType(equalFieldType.typeClass);
|
|
1582
|
+
} else throw new Error(`Neither filter-prop nor root-prop with name: ${rootQueryArg} for asyncSelect-query not found. Consider setting filterField.gqlVarName explicitly.`);
|
|
1583
|
+
}
|
|
1584
|
+
if (config.filter.type === "formProp") formProps.push({
|
|
1585
|
+
name: config.filter.propName,
|
|
1586
|
+
optional: false,
|
|
1587
|
+
type: filterVarType
|
|
1588
|
+
});
|
|
1589
|
+
return {
|
|
1590
|
+
filterField,
|
|
1591
|
+
filterType,
|
|
1592
|
+
filterVarName,
|
|
1593
|
+
filterVarValue
|
|
1594
|
+
};
|
|
1595
|
+
})() : void 0;
|
|
1596
|
+
if (filterConfig) {
|
|
1597
|
+
imports.push({
|
|
1598
|
+
name: "OnChangeField",
|
|
1599
|
+
importPath: "@comet/admin"
|
|
1600
|
+
});
|
|
1601
|
+
finalFormConfig = {
|
|
1602
|
+
subscription: { values: true },
|
|
1603
|
+
renderProps: {
|
|
1604
|
+
values: true,
|
|
1605
|
+
form: true
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
if (config.type != "asyncSelectFilter") if (!multiple) if (!required) formValueConfig.formValueToGqlInputCode = `$fieldName ? $fieldName.id : null`;
|
|
1610
|
+
else formValueConfig.formValueToGqlInputCode = `$fieldName?.id`;
|
|
1611
|
+
else formValueConfig.formValueToGqlInputCode = `$fieldName.map((item) => item.id)`;
|
|
1612
|
+
imports.push({
|
|
1613
|
+
name: `GQL${queryName}Query`,
|
|
1614
|
+
importPath: `./${baseOutputFilename}.generated`
|
|
1615
|
+
});
|
|
1616
|
+
imports.push({
|
|
1617
|
+
name: `GQL${queryName}QueryVariables`,
|
|
1618
|
+
importPath: `./${baseOutputFilename}.generated`
|
|
1619
|
+
});
|
|
1620
|
+
if (useAutocomplete) imports.push({
|
|
1621
|
+
name: "AsyncAutocompleteField",
|
|
1622
|
+
importPath: "@comet/admin"
|
|
1623
|
+
});
|
|
1624
|
+
else imports.push({
|
|
1625
|
+
name: "AsyncSelectField",
|
|
1626
|
+
importPath: "@comet/admin"
|
|
1627
|
+
});
|
|
1628
|
+
const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
|
|
1629
|
+
if (config.type == "asyncSelectFilter") {
|
|
1630
|
+
formValueConfig.typeCode = {
|
|
1631
|
+
nullable: true,
|
|
1632
|
+
type: `{ id: string; ${labelField}: string }`
|
|
1633
|
+
};
|
|
1634
|
+
formValueConfig.initializationCode = `data.${instanceGqlType}.${config.loadValueQueryField.replace(/\./g, "?.")}`;
|
|
1635
|
+
}
|
|
1636
|
+
code = `<${useAutocomplete ? "AsyncAutocompleteField" : "AsyncSelectField"}
|
|
1637
|
+
${required ? "required" : ""}
|
|
1638
|
+
variant="horizontal"
|
|
1639
|
+
fullWidth
|
|
1640
|
+
${config.readOnly ? "readOnly disabled" : ""}
|
|
1641
|
+
${multiple ? "multiple" : ""}
|
|
1642
|
+
name="${nameWithPrefix}"
|
|
1643
|
+
label={${fieldLabel}}
|
|
1644
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1645
|
+
loadOptions={async (${useAutocomplete ? `search?: string` : ""}) => {
|
|
1646
|
+
const { data } = await client.query<GQL${queryName}Query, GQL${queryName}QueryVariables>({
|
|
1647
|
+
query: gql\`${generateGqlOperation({
|
|
1648
|
+
type: "query",
|
|
1649
|
+
operationName: queryName,
|
|
1650
|
+
rootOperation: rootQuery,
|
|
1651
|
+
fields: ["nodes.id", `nodes.${labelField}`],
|
|
1652
|
+
variables: [useAutocomplete ? {
|
|
1653
|
+
name: "search",
|
|
1654
|
+
type: "String"
|
|
1655
|
+
} : void 0, filterConfig ? {
|
|
1656
|
+
name: filterConfig.filterVarName,
|
|
1657
|
+
type: filterConfig.filterType.typeClass + (filterConfig.filterType.required ? `!` : ``)
|
|
1658
|
+
} : void 0].filter((v) => v !== void 0)
|
|
1659
|
+
})}\`${filterConfig || useAutocomplete ? ", variables: { " : ""}
|
|
1660
|
+
${filterConfig ? `${filterConfig.filterVarName}: ${filterConfig.filterVarValue},` : ``}
|
|
1661
|
+
${useAutocomplete ? `search,` : ``}
|
|
1662
|
+
${filterConfig || useAutocomplete ? " }" : ""}
|
|
1663
|
+
});
|
|
1664
|
+
return data.${rootQuery}.nodes;
|
|
1665
|
+
}}
|
|
1666
|
+
getOptionLabel={(option) => option.${labelField}}
|
|
1667
|
+
${filterConfig?.filterField ? `disabled={!values?.${String(filterConfig.filterField.name)}}` : ``}
|
|
1668
|
+
/>${filterConfig?.filterField ? `<OnChangeField name="${String(filterConfig.filterField.name)}">
|
|
1669
|
+
{(value, previousValue) => {
|
|
1670
|
+
if (value.id !== previousValue.id) {
|
|
1671
|
+
form.change("${String(config.name)}", undefined);
|
|
1672
|
+
}
|
|
1673
|
+
}}
|
|
1674
|
+
</OnChangeField>` : ``}`;
|
|
1675
|
+
return {
|
|
1676
|
+
code,
|
|
1677
|
+
hooksCode: "",
|
|
1678
|
+
formFragmentFields,
|
|
1679
|
+
gqlDocuments: {},
|
|
1680
|
+
imports,
|
|
1681
|
+
formProps,
|
|
1682
|
+
formValuesConfig: [formValueConfig],
|
|
1683
|
+
finalFormConfig
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
//#endregion
|
|
1688
|
+
//#region src/commands/generate/generateForm/formField/findIntrospectionFieldType.ts
|
|
1689
|
+
function findIntrospectionFieldType({ name, gqlType, gqlIntrospection }) {
|
|
1690
|
+
let introspectionFieldType;
|
|
1691
|
+
for (const namePart of name.split(".")) {
|
|
1692
|
+
const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
1693
|
+
if (!introspectionObject) throw new Error(`didn't find object ${gqlType} in gql introspection`);
|
|
1694
|
+
const introspectionField = introspectionObject.fields.find((field) => field.name === namePart);
|
|
1695
|
+
introspectionFieldType = introspectionField ? introspectionField.type.kind === "NON_NULL" ? introspectionField.type.ofType : introspectionField.type : void 0;
|
|
1696
|
+
if (introspectionFieldType?.kind === "OBJECT") gqlType = introspectionFieldType.name;
|
|
1697
|
+
}
|
|
1698
|
+
return introspectionFieldType;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region src/commands/generate/generateForm/generateFormField.ts
|
|
1703
|
+
function generateFormField({ gqlIntrospection, baseOutputFilename, config, formConfig, gqlType, namePrefix }) {
|
|
1704
|
+
if (config.type == "asyncSelect" || config.type == "asyncSelectFilter") return generateAsyncSelect({
|
|
1705
|
+
gqlIntrospection,
|
|
1706
|
+
baseOutputFilename,
|
|
1707
|
+
config,
|
|
1708
|
+
formConfig,
|
|
1709
|
+
gqlType,
|
|
1710
|
+
namePrefix
|
|
1711
|
+
});
|
|
1712
|
+
const imports = [];
|
|
1713
|
+
const formProps = [];
|
|
1714
|
+
const { name, formattedMessageRootId, fieldLabel, startAdornment, endAdornment, imports: optionsImports } = buildFormFieldOptions({
|
|
1715
|
+
config,
|
|
1716
|
+
formConfig
|
|
1717
|
+
});
|
|
1718
|
+
imports.push(...optionsImports);
|
|
1719
|
+
let introspectionFieldType = findIntrospectionFieldType({
|
|
1720
|
+
name,
|
|
1721
|
+
gqlIntrospection,
|
|
1722
|
+
gqlType
|
|
1723
|
+
});
|
|
1724
|
+
const nameWithPrefix = `${namePrefix ? `${namePrefix}.` : ``}${name}`;
|
|
1725
|
+
const rootGqlType = formConfig.gqlType;
|
|
1726
|
+
const dataRootName = rootGqlType[0].toLowerCase() + rootGqlType.substring(1);
|
|
1727
|
+
const required = !isFieldOptional({
|
|
1728
|
+
config,
|
|
1729
|
+
gqlIntrospection,
|
|
1730
|
+
gqlType
|
|
1731
|
+
});
|
|
1732
|
+
const endAdornmentWithLockIconProp = `endAdornment={<InputAdornment position="end"><Lock /></InputAdornment>}`;
|
|
1733
|
+
const readOnlyProps = `readOnly disabled`;
|
|
1734
|
+
const readOnlyPropsWithLock = `${readOnlyProps} ${endAdornmentWithLockIconProp}`;
|
|
1735
|
+
const formValueConfig = { fieldName: nameWithPrefix };
|
|
1736
|
+
const gqlDocuments = {};
|
|
1737
|
+
const hooksCode = "";
|
|
1738
|
+
let finalFormConfig;
|
|
1739
|
+
let validateCode = "";
|
|
1740
|
+
if (config.validate) {
|
|
1741
|
+
if (isGeneratorConfigImport(config.validate)) {
|
|
1742
|
+
imports.push(convertConfigImport(config.validate));
|
|
1743
|
+
validateCode = `validate={${config.validate.name}}`;
|
|
1744
|
+
} else if (isGeneratorConfigCode(config.validate)) {
|
|
1745
|
+
validateCode = `validate={${config.validate.code}}`;
|
|
1746
|
+
imports.push(...config.validate.imports.map((imprt) => convertConfigImport(imprt)));
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
let code = "";
|
|
1750
|
+
let formFragmentFields = [name];
|
|
1751
|
+
if (config.type == "text") {
|
|
1752
|
+
code = `
|
|
1753
|
+
<${config.multiline ? "TextAreaField" : "TextField"}
|
|
1754
|
+
${required ? "required" : ""}
|
|
1755
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1756
|
+
variant="horizontal"
|
|
1757
|
+
fullWidth
|
|
1758
|
+
name="${nameWithPrefix}"
|
|
1759
|
+
label={${fieldLabel}}
|
|
1760
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1761
|
+
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1762
|
+
${config.helperText ? `helperText={${generateFormattedMessage({
|
|
1763
|
+
config: config.helperText,
|
|
1764
|
+
id: `${formattedMessageRootId}.${name}.helperText`,
|
|
1765
|
+
type: "jsx"
|
|
1766
|
+
})}}` : ""}
|
|
1767
|
+
${validateCode}
|
|
1768
|
+
/>`;
|
|
1769
|
+
if (!required && !config.readOnly) formValueConfig.formValueToGqlInputCode = `$fieldName ?? null`;
|
|
1770
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = JSON.stringify(config.initialValue);
|
|
1771
|
+
} else if (config.type == "number") {
|
|
1772
|
+
code = `
|
|
1773
|
+
<NumberField
|
|
1774
|
+
${required ? "required" : ""}
|
|
1775
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1776
|
+
variant="horizontal"
|
|
1777
|
+
fullWidth
|
|
1778
|
+
name="${nameWithPrefix}"
|
|
1779
|
+
label={${fieldLabel}}
|
|
1780
|
+
${config.decimals ? `decimals={${config.decimals}}` : ""}
|
|
1781
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1782
|
+
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1783
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1784
|
+
${validateCode}
|
|
1785
|
+
/>`;
|
|
1786
|
+
if (!required && !config.readOnly) formValueConfig.formValueToGqlInputCode = `$fieldName ?? null`;
|
|
1787
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = JSON.stringify(config.initialValue);
|
|
1788
|
+
} else if (config.type === "numberRange") {
|
|
1789
|
+
code = `
|
|
1790
|
+
<Field
|
|
1791
|
+
${required ? "required" : ""}
|
|
1792
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1793
|
+
variant="horizontal"
|
|
1794
|
+
fullWidth
|
|
1795
|
+
name="${nameWithPrefix}"
|
|
1796
|
+
component={FinalFormRangeInput}
|
|
1797
|
+
label={${fieldLabel}}
|
|
1798
|
+
min={${config.minValue}}
|
|
1799
|
+
max={${config.maxValue}}
|
|
1800
|
+
${config.disableSlider ? "disableSlider" : ""}
|
|
1801
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1802
|
+
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1803
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1804
|
+
${validateCode}
|
|
1805
|
+
/>`;
|
|
1806
|
+
formFragmentFields = [`${name}.min`, `${name}.max`];
|
|
1807
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = JSON.stringify(config.initialValue);
|
|
1808
|
+
} else if (config.type == "boolean") {
|
|
1809
|
+
const checkboxLabel = config.checkboxLabel ? generateFormattedMessage({
|
|
1810
|
+
config: config.checkboxLabel,
|
|
1811
|
+
id: `${formattedMessageRootId}.${name}.checkboxLabel`,
|
|
1812
|
+
type: "jsx"
|
|
1813
|
+
}) : "";
|
|
1814
|
+
code = `<CheckboxField
|
|
1815
|
+
fieldLabel={${fieldLabel}}
|
|
1816
|
+
${config.checkboxLabel ? `label={${checkboxLabel}}` : ""}
|
|
1817
|
+
name="${nameWithPrefix}"
|
|
1818
|
+
fullWidth
|
|
1819
|
+
variant="horizontal"
|
|
1820
|
+
${config.readOnly ? readOnlyProps : ""}
|
|
1821
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1822
|
+
${validateCode}
|
|
1823
|
+
/>`;
|
|
1824
|
+
formValueConfig.defaultInitializationCode = config.initialValue ? "true" : "false";
|
|
1825
|
+
} else if (config.type == "date") {
|
|
1826
|
+
imports.push({
|
|
1827
|
+
name: "DatePickerField",
|
|
1828
|
+
importPath: "@comet/admin"
|
|
1829
|
+
});
|
|
1830
|
+
code = `
|
|
1831
|
+
<DatePickerField
|
|
1832
|
+
${required ? "required" : ""}
|
|
1833
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1834
|
+
variant="horizontal"
|
|
1835
|
+
fullWidth
|
|
1836
|
+
name="${nameWithPrefix}"
|
|
1837
|
+
label={${fieldLabel}}
|
|
1838
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1839
|
+
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1840
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1841
|
+
${validateCode}
|
|
1842
|
+
/>`;
|
|
1843
|
+
if (!required && !config.readOnly) formValueConfig.formValueToGqlInputCode = `$fieldName ?? null`;
|
|
1844
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = JSON.stringify(config.initialValue);
|
|
1845
|
+
} else if (config.type == "dateTime") {
|
|
1846
|
+
imports.push({
|
|
1847
|
+
name: "DateTimePickerField",
|
|
1848
|
+
importPath: "@comet/admin"
|
|
1849
|
+
});
|
|
1850
|
+
code = `<DateTimePickerField
|
|
1851
|
+
${required ? "required" : ""}
|
|
1852
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1853
|
+
variant="horizontal"
|
|
1854
|
+
fullWidth
|
|
1855
|
+
name="${nameWithPrefix}"
|
|
1856
|
+
label={${fieldLabel}}
|
|
1857
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1858
|
+
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1859
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1860
|
+
${validateCode}
|
|
1861
|
+
/>`;
|
|
1862
|
+
formValueConfig.initializationCode = `data.${dataRootName}.${nameWithPrefix} ? new Date(data.${dataRootName}.${nameWithPrefix}) : undefined`;
|
|
1863
|
+
formValueConfig.omitFromFragmentType = true;
|
|
1864
|
+
formValueConfig.typeCode = {
|
|
1865
|
+
nullable: !required,
|
|
1866
|
+
type: `Date${!required ? " | null" : ""}`
|
|
1867
|
+
};
|
|
1868
|
+
if (!config.readOnly) formValueConfig.formValueToGqlInputCode = required ? `$fieldName.toISOString()` : `$fieldName ? $fieldName.toISOString() : null`;
|
|
1869
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = `new Date("${config.initialValue.toISOString()}")`;
|
|
1870
|
+
} else if (config.type == "block") {
|
|
1871
|
+
code = `<Field name="${nameWithPrefix}" isEqual={isEqual} label={${fieldLabel}} variant="horizontal" fullWidth>
|
|
1872
|
+
{createFinalFormBlock(rootBlocks.${String(config.name)})}
|
|
1873
|
+
</Field>`;
|
|
1874
|
+
formValueConfig.formValueToGqlInputCode = `rootBlocks.${name}.state2Output($fieldName)`;
|
|
1875
|
+
formValueConfig.omitFromFragmentType = true;
|
|
1876
|
+
formValueConfig.typeCode = {
|
|
1877
|
+
nullable: false,
|
|
1878
|
+
type: `BlockState<typeof rootBlocks.${name}>`
|
|
1879
|
+
};
|
|
1880
|
+
formValueConfig.initializationCode = `rootBlocks.${name}.input2State(data.${dataRootName}.${nameWithPrefix})`;
|
|
1881
|
+
formValueConfig.defaultInitializationCode = `rootBlocks.${name}.defaultValues()`;
|
|
1882
|
+
} else if (config.type === "fileUpload") {
|
|
1883
|
+
const multiple = config.multiple || typeof config.maxFiles === "number" && config.maxFiles > 1;
|
|
1884
|
+
code = `<FileUploadField name="${name}" label={${fieldLabel}}
|
|
1885
|
+
variant="horizontal"
|
|
1886
|
+
${config.multiple ? "multiple" : ""}
|
|
1887
|
+
${config.maxFiles ? `maxFiles={${config.maxFiles}}` : ""}
|
|
1888
|
+
${config.maxFileSize ? `maxFileSize={${config.maxFileSize}}` : ""}
|
|
1889
|
+
${config.readOnly ? `readOnly` : ""}
|
|
1890
|
+
${config.layout ? `layout="${config.layout}"` : ""}
|
|
1891
|
+
${config.accept ? `accept="${config.accept}"` : ""}
|
|
1892
|
+
/>`;
|
|
1893
|
+
if (multiple) formValueConfig.formValueToGqlInputCode = `$fieldName?.map(({ id }) => id)`;
|
|
1894
|
+
else formValueConfig.formValueToGqlInputCode = `$fieldName ? $fieldName.id : null`;
|
|
1895
|
+
formFragmentFields = [`${name}...${config.download ? "FinalFormFileUploadDownloadable" : "FinalFormFileUpload"}`];
|
|
1896
|
+
} else if (config.type == "staticSelect") {
|
|
1897
|
+
const multiple = introspectionFieldType?.kind === "LIST";
|
|
1898
|
+
if (introspectionFieldType?.kind === "LIST") introspectionFieldType = introspectionFieldType.ofType.kind === "NON_NULL" ? introspectionFieldType.ofType.ofType : introspectionFieldType.ofType;
|
|
1899
|
+
const enumType = gqlIntrospection.__schema.types.find((t) => t.kind === "ENUM" && t.name === introspectionFieldType.name);
|
|
1900
|
+
if (!enumType) throw new Error(`Enum type ${introspectionFieldType.name} not found for field ${name}`);
|
|
1901
|
+
const values = (config.values ? config.values : enumType.enumValues.map((i) => i.name)).map((value) => {
|
|
1902
|
+
if (typeof value === "string") {
|
|
1903
|
+
const messageId = `${formattedMessageRootId}.${name}.${value.charAt(0).toLowerCase() + value.slice(1)}`;
|
|
1904
|
+
return {
|
|
1905
|
+
value,
|
|
1906
|
+
labelCode: generateFormattedMessage({
|
|
1907
|
+
config: void 0,
|
|
1908
|
+
defaultMessage: camelCaseToHumanReadable(value),
|
|
1909
|
+
id: messageId,
|
|
1910
|
+
type: "jsx"
|
|
1911
|
+
})
|
|
1912
|
+
};
|
|
1913
|
+
} else {
|
|
1914
|
+
const messageId = `${formattedMessageRootId}.${name}.${value.value.charAt(0).toLowerCase() + value.value.slice(1)}`;
|
|
1915
|
+
const valueLabel = generateFormattedMessage({
|
|
1916
|
+
config: value.label,
|
|
1917
|
+
id: messageId,
|
|
1918
|
+
type: "jsx"
|
|
1919
|
+
});
|
|
1920
|
+
return {
|
|
1921
|
+
value: value.value,
|
|
1922
|
+
labelCode: valueLabel
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
let inputType = config.inputType;
|
|
1927
|
+
if (!inputType) if (!required || multiple) inputType = "select";
|
|
1928
|
+
else if (values.length <= 5) inputType = "radio";
|
|
1929
|
+
else inputType = "select";
|
|
1930
|
+
if (inputType === "radio" && multiple) throw new Error(`${name}: inputType=radio doesn't support multiple`);
|
|
1931
|
+
if (inputType === "radio" && !required) throw new Error(`${name}: inputType=radio must be required as it doesn't support clearable`);
|
|
1932
|
+
if (inputType === "radio") code = `<RadioGroupField
|
|
1933
|
+
${required ? "required" : ""}
|
|
1934
|
+
variant="horizontal"
|
|
1935
|
+
fullWidth
|
|
1936
|
+
name="${nameWithPrefix}"
|
|
1937
|
+
label={${fieldLabel}}
|
|
1938
|
+
options={[
|
|
1939
|
+
${values.map((value) => {
|
|
1940
|
+
return `{
|
|
1941
|
+
label: ${value.labelCode},
|
|
1942
|
+
value: "${value.value}",
|
|
1943
|
+
}`;
|
|
1944
|
+
}).join(",")}
|
|
1945
|
+
]}/>`;
|
|
1946
|
+
else {
|
|
1947
|
+
imports.push({
|
|
1948
|
+
name: "SelectField",
|
|
1949
|
+
importPath: "@comet/admin"
|
|
1950
|
+
});
|
|
1951
|
+
code = `<SelectField
|
|
1952
|
+
${required ? "required" : ""}
|
|
1953
|
+
fullWidth
|
|
1954
|
+
variant={"horizontal"}
|
|
1955
|
+
name="${nameWithPrefix}"
|
|
1956
|
+
label={${fieldLabel}}
|
|
1957
|
+
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
1958
|
+
${config.helperText ? `helperText={<FormattedMessage id="${formattedMessageRootId}.${name}.helperText" defaultMessage="${config.helperText}" />}` : ""}
|
|
1959
|
+
${validateCode}
|
|
1960
|
+
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
1961
|
+
${multiple ? "multiple" : ""}
|
|
1962
|
+
options={[${values.map((value) => {
|
|
1963
|
+
return `{
|
|
1964
|
+
value: "${value.value}",
|
|
1965
|
+
label: ${value.labelCode}
|
|
1966
|
+
}`;
|
|
1967
|
+
})}]}
|
|
1968
|
+
/>`;
|
|
1969
|
+
}
|
|
1970
|
+
if (config.initialValue !== void 0) formValueConfig.defaultInitializationCode = JSON.stringify(config.initialValue);
|
|
1971
|
+
} else throw new Error(`Unsupported type`);
|
|
1972
|
+
return {
|
|
1973
|
+
code,
|
|
1974
|
+
hooksCode,
|
|
1975
|
+
formFragmentFields,
|
|
1976
|
+
gqlDocuments,
|
|
1977
|
+
imports,
|
|
1978
|
+
formProps,
|
|
1979
|
+
formValuesConfig: [formValueConfig],
|
|
1980
|
+
finalFormConfig
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
//#endregion
|
|
1985
|
+
//#region src/commands/generate/generateForm/generateFormLayout.ts
|
|
1986
|
+
function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, formFragmentName, formConfig, gqlType, namePrefix }) {
|
|
1987
|
+
const rootGqlType = formConfig.gqlType;
|
|
1988
|
+
const formattedMessageRootId = rootGqlType[0].toLowerCase() + rootGqlType.substring(1);
|
|
1989
|
+
const dataRootName = rootGqlType[0].toLowerCase() + rootGqlType.substring(1);
|
|
1990
|
+
let code = "";
|
|
1991
|
+
let hooksCode = "";
|
|
1992
|
+
const formFragmentFields = [];
|
|
1993
|
+
const gqlDocuments = {};
|
|
1994
|
+
const imports = [];
|
|
1995
|
+
const formProps = [];
|
|
1996
|
+
const formValuesConfig = [];
|
|
1997
|
+
const finalFormConfig = {
|
|
1998
|
+
subscription: {},
|
|
1999
|
+
renderProps: {}
|
|
2000
|
+
};
|
|
2001
|
+
if (config.type === "fieldSet") {
|
|
2002
|
+
const generatedFields = generateFields({
|
|
2003
|
+
gqlIntrospection,
|
|
2004
|
+
baseOutputFilename,
|
|
2005
|
+
fields: config.fields,
|
|
2006
|
+
formFragmentName,
|
|
2007
|
+
formConfig,
|
|
2008
|
+
gqlType,
|
|
2009
|
+
namePrefix
|
|
2010
|
+
});
|
|
2011
|
+
hooksCode += generatedFields.hooksCode;
|
|
2012
|
+
formFragmentFields.push(...generatedFields.formFragmentFields);
|
|
2013
|
+
for (const name in generatedFields.gqlDocuments) gqlDocuments[name] = generatedFields.gqlDocuments[name];
|
|
2014
|
+
imports.push(...generatedFields.imports);
|
|
2015
|
+
formProps.push(...generatedFields.formProps);
|
|
2016
|
+
formValuesConfig.push(...generatedFields.formValuesConfig);
|
|
2017
|
+
finalFormConfig.subscription = {
|
|
2018
|
+
...finalFormConfig.subscription,
|
|
2019
|
+
...generatedFields.finalFormConfig?.subscription
|
|
2020
|
+
};
|
|
2021
|
+
finalFormConfig.renderProps = {
|
|
2022
|
+
...finalFormConfig.renderProps,
|
|
2023
|
+
...generatedFields.finalFormConfig?.renderProps
|
|
2024
|
+
};
|
|
2025
|
+
imports.push({
|
|
2026
|
+
name: "FieldSet",
|
|
2027
|
+
importPath: "@comet/admin"
|
|
2028
|
+
});
|
|
2029
|
+
const supportPlaceholder = config.supportText?.includes("{");
|
|
2030
|
+
if (supportPlaceholder) imports.push({
|
|
2031
|
+
name: "FormSpy",
|
|
2032
|
+
importPath: "react-final-form"
|
|
2033
|
+
});
|
|
2034
|
+
code = `
|
|
2035
|
+
<FieldSet
|
|
2036
|
+
${config.collapsible === void 0 || config.collapsible ? `collapsible` : ``}
|
|
2037
|
+
${config.initiallyExpanded != null ? `initiallyExpanded={${config.initiallyExpanded}}` : ``}
|
|
2038
|
+
title={${generateFormattedMessage({
|
|
2039
|
+
config: config.title,
|
|
2040
|
+
id: `${formattedMessageRootId}.${config.name}.title`,
|
|
2041
|
+
defaultMessage: camelCaseToHumanReadable(config.name),
|
|
2042
|
+
type: "jsx"
|
|
2043
|
+
})}}
|
|
2044
|
+
${config.supportText ? `supportText={
|
|
2045
|
+
${supportPlaceholder ? `mode === "edit" && (<FormSpy subscription={{ values: true }}>{({ values }) => (` : ``}
|
|
2046
|
+
<FormattedMessage
|
|
2047
|
+
id="${formattedMessageRootId}.${config.name}.supportText"
|
|
2048
|
+
defaultMessage="${config.supportText}"
|
|
2049
|
+
${supportPlaceholder ? `values={{ ...values }}` : ``}
|
|
2050
|
+
/>
|
|
2051
|
+
${supportPlaceholder ? `)}</FormSpy>)` : ``}
|
|
2052
|
+
}` : ``}
|
|
2053
|
+
>
|
|
2054
|
+
${generatedFields.code}
|
|
2055
|
+
</FieldSet>`;
|
|
2056
|
+
} else if (config.type === "optionalNestedFields") {
|
|
2057
|
+
const name = String(config.name);
|
|
2058
|
+
const introspectionFieldType = findIntrospectionFieldType({
|
|
2059
|
+
name,
|
|
2060
|
+
gqlType,
|
|
2061
|
+
gqlIntrospection
|
|
2062
|
+
});
|
|
2063
|
+
if (!introspectionFieldType) throw new Error(`field ${name} in gql introspection type ${gqlType} not found`);
|
|
2064
|
+
if (introspectionFieldType.kind !== "OBJECT") throw new Error(`field ${name} in gql introspection type ${gqlType} has to be OBJECT`);
|
|
2065
|
+
const generatedFields = generateFields({
|
|
2066
|
+
gqlIntrospection,
|
|
2067
|
+
baseOutputFilename,
|
|
2068
|
+
fields: config.fields,
|
|
2069
|
+
formFragmentName,
|
|
2070
|
+
formConfig,
|
|
2071
|
+
gqlType: introspectionFieldType.name,
|
|
2072
|
+
namePrefix: name
|
|
2073
|
+
});
|
|
2074
|
+
hooksCode += generatedFields.hooksCode;
|
|
2075
|
+
formFragmentFields.push(...generatedFields.formFragmentFields.map((field) => `${name}.${field}`));
|
|
2076
|
+
for (const name$1 in generatedFields.gqlDocuments) gqlDocuments[name$1] = generatedFields.gqlDocuments[name$1];
|
|
2077
|
+
imports.push(...generatedFields.imports);
|
|
2078
|
+
formValuesConfig.push(...generatedFields.formValuesConfig);
|
|
2079
|
+
formValuesConfig.push({
|
|
2080
|
+
fieldName: `${name}Enabled`,
|
|
2081
|
+
omitFromFragmentType: false,
|
|
2082
|
+
destructFromFormValues: true,
|
|
2083
|
+
typeCode: {
|
|
2084
|
+
nullable: false,
|
|
2085
|
+
type: "boolean"
|
|
2086
|
+
},
|
|
2087
|
+
initializationCode: `!!data.${dataRootName}.${name}`
|
|
2088
|
+
});
|
|
2089
|
+
formValuesConfig.push({
|
|
2090
|
+
fieldName: `${name}`,
|
|
2091
|
+
wrapFormValueToGqlInputCode: `${name.split(".").pop()}Enabled && $fieldName ? $inner : null`
|
|
2092
|
+
});
|
|
2093
|
+
imports.push({
|
|
2094
|
+
name: "FinalFormSwitch",
|
|
2095
|
+
importPath: "@comet/admin"
|
|
2096
|
+
});
|
|
2097
|
+
imports.push({
|
|
2098
|
+
name: "messages",
|
|
2099
|
+
importPath: "@comet/admin"
|
|
2100
|
+
});
|
|
2101
|
+
imports.push({
|
|
2102
|
+
name: "FormControlLabel",
|
|
2103
|
+
importPath: "@mui/material"
|
|
2104
|
+
});
|
|
2105
|
+
code = `<Field
|
|
2106
|
+
fullWidth
|
|
2107
|
+
name="${String(config.name)}Enabled"
|
|
2108
|
+
type="checkbox"
|
|
2109
|
+
label={${generateFormattedMessage({
|
|
2110
|
+
config: config.checkboxLabel,
|
|
2111
|
+
id: `${formattedMessageRootId}.${String(config.name)}.${String(config.name)}Enabled`,
|
|
2112
|
+
defaultMessage: `Enable ${camelCaseToHumanReadable(String(config.name))}`,
|
|
2113
|
+
type: "jsx"
|
|
2114
|
+
})}}
|
|
2115
|
+
>
|
|
2116
|
+
{(props) => (
|
|
2117
|
+
<FormControlLabel
|
|
2118
|
+
control={<FinalFormSwitch {...props} />}
|
|
2119
|
+
label={props.input.checked ? <FormattedMessage {...messages.yes} /> : <FormattedMessage {...messages.no} />}
|
|
2120
|
+
/>
|
|
2121
|
+
)}
|
|
2122
|
+
</Field>
|
|
2123
|
+
<Field name="${String(config.name)}Enabled" fullWidth subscription={{ value: true }}>
|
|
2124
|
+
{({ input: { value } }) =>
|
|
2125
|
+
value ? (
|
|
2126
|
+
<>
|
|
2127
|
+
${generatedFields.code}
|
|
2128
|
+
</>
|
|
2129
|
+
) : null
|
|
2130
|
+
}
|
|
2131
|
+
</Field>`;
|
|
2132
|
+
} else throw new Error(`Unsupported type`);
|
|
2133
|
+
return {
|
|
2134
|
+
code,
|
|
2135
|
+
hooksCode,
|
|
2136
|
+
formFragmentFields,
|
|
2137
|
+
gqlDocuments,
|
|
2138
|
+
imports,
|
|
2139
|
+
formProps,
|
|
2140
|
+
formValuesConfig,
|
|
2141
|
+
finalFormConfig
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
//#endregion
|
|
2146
|
+
//#region src/commands/generate/generateForm/generateFields.ts
|
|
2147
|
+
function findFieldByName(name, fields) {
|
|
2148
|
+
return fields.reduce((acc, field) => {
|
|
2149
|
+
return acc ? acc : "name" in field && field.name === name ? field : isFormLayoutConfig(field) ? findFieldByName(name, field.fields) : void 0;
|
|
2150
|
+
}, void 0);
|
|
2151
|
+
}
|
|
2152
|
+
function generateFields({ gqlIntrospection, baseOutputFilename, fields, formFragmentName, formConfig, gqlType, namePrefix }) {
|
|
2153
|
+
const gqlDocuments = {};
|
|
2154
|
+
let hooksCode = "";
|
|
2155
|
+
const formFragmentFields = [];
|
|
2156
|
+
const imports = [];
|
|
2157
|
+
const formProps = [];
|
|
2158
|
+
const formValuesConfig = [];
|
|
2159
|
+
const finalFormConfig = {
|
|
2160
|
+
subscription: {},
|
|
2161
|
+
renderProps: {}
|
|
2162
|
+
};
|
|
2163
|
+
return {
|
|
2164
|
+
code: fields.map((field) => {
|
|
2165
|
+
let generated;
|
|
2166
|
+
if (field.type === "component") generated = generateComponentFormField({ config: field });
|
|
2167
|
+
else if (isFormFieldConfig(field)) generated = generateFormField({
|
|
2168
|
+
gqlIntrospection,
|
|
2169
|
+
baseOutputFilename,
|
|
2170
|
+
formFragmentName,
|
|
2171
|
+
config: field,
|
|
2172
|
+
formConfig,
|
|
2173
|
+
gqlType,
|
|
2174
|
+
namePrefix
|
|
2175
|
+
});
|
|
2176
|
+
else if (isFormLayoutConfig(field)) generated = generateFormLayout({
|
|
2177
|
+
gqlIntrospection,
|
|
2178
|
+
baseOutputFilename,
|
|
2179
|
+
formFragmentName,
|
|
2180
|
+
config: field,
|
|
2181
|
+
formConfig,
|
|
2182
|
+
gqlType,
|
|
2183
|
+
namePrefix
|
|
2184
|
+
});
|
|
2185
|
+
else throw new Error("Not supported config");
|
|
2186
|
+
for (const name in generated.gqlDocuments) gqlDocuments[name] = generated.gqlDocuments[name];
|
|
2187
|
+
imports.push(...generated.imports);
|
|
2188
|
+
formProps.push(...generated.formProps);
|
|
2189
|
+
hooksCode += generated.hooksCode;
|
|
2190
|
+
formFragmentFields.push(...generated.formFragmentFields);
|
|
2191
|
+
formValuesConfig.push(...generated.formValuesConfig);
|
|
2192
|
+
finalFormConfig.subscription = {
|
|
2193
|
+
...finalFormConfig.subscription,
|
|
2194
|
+
...generated.finalFormConfig?.subscription
|
|
2195
|
+
};
|
|
2196
|
+
finalFormConfig.renderProps = {
|
|
2197
|
+
...finalFormConfig.renderProps,
|
|
2198
|
+
...generated.finalFormConfig?.renderProps
|
|
2199
|
+
};
|
|
2200
|
+
return generated.code;
|
|
2201
|
+
}).join("\n"),
|
|
2202
|
+
hooksCode,
|
|
2203
|
+
formFragmentFields,
|
|
2204
|
+
gqlDocuments,
|
|
2205
|
+
imports,
|
|
2206
|
+
formProps,
|
|
2207
|
+
formValuesConfig,
|
|
2208
|
+
finalFormConfig
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
//#endregion
|
|
2213
|
+
//#region src/commands/generate/generateForm/generateFormValues.ts
|
|
2214
|
+
function formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType }) {
|
|
2215
|
+
const treeRoot = { children: {} };
|
|
2216
|
+
for (const formValueConfig of formValuesConfig) {
|
|
2217
|
+
const fieldName = formValueConfig.fieldName;
|
|
2218
|
+
let currentTreeNode = treeRoot;
|
|
2219
|
+
let currentGqlType = gqlType;
|
|
2220
|
+
for (const part of fieldName.split(".")) {
|
|
2221
|
+
const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === currentGqlType);
|
|
2222
|
+
if (!introspectionObject) throw new Error(`didn't find object ${gqlType} in gql introspection`);
|
|
2223
|
+
const introspectionField = introspectionObject.fields.find((field) => field.name === part);
|
|
2224
|
+
const introspectionFieldType = introspectionField ? introspectionField.type.kind === "NON_NULL" ? introspectionField.type.ofType : introspectionField.type : void 0;
|
|
2225
|
+
if (introspectionFieldType?.kind === "OBJECT") currentGqlType = introspectionFieldType.name;
|
|
2226
|
+
if (!currentTreeNode.children[part]) currentTreeNode.children[part] = { children: {} };
|
|
2227
|
+
currentTreeNode.children[part].nullable = introspectionField?.type.kind !== "NON_NULL";
|
|
2228
|
+
currentTreeNode = currentTreeNode.children[part];
|
|
2229
|
+
}
|
|
2230
|
+
currentTreeNode.config = formValueConfig;
|
|
2231
|
+
}
|
|
2232
|
+
return treeRoot.children;
|
|
2233
|
+
}
|
|
2234
|
+
function generateFormValuesTypeFromTree(tree, currentTypeName, currentIsNullable) {
|
|
2235
|
+
const omitKeys = [];
|
|
2236
|
+
let appendCode = "";
|
|
2237
|
+
for (const [key, value] of Object.entries(tree)) if (Object.keys(value.children).length > 0) {
|
|
2238
|
+
let childRootType = `${currentTypeName}["${key}"]`;
|
|
2239
|
+
if (currentIsNullable) childRootType = `NonNullable<${currentTypeName}>["${key}"]`;
|
|
2240
|
+
const childOmit = generateFormValuesTypeFromTree(value.children, childRootType, value.nullable ?? false);
|
|
2241
|
+
if (childOmit !== childRootType) {
|
|
2242
|
+
appendCode += `${key}: ${childOmit}`;
|
|
2243
|
+
omitKeys.push(key);
|
|
2244
|
+
}
|
|
2245
|
+
if (value.config?.typeCode) throw new Error("Field has both subfields and direct typeCode, which is not supported.");
|
|
2246
|
+
if (value.config?.omitFromFragmentType) throw new Error("Field has both subfields and direct omitFromFragmentType, which is not supported.");
|
|
2247
|
+
} else if (value.config) {
|
|
2248
|
+
if (value.config.typeCode) appendCode += `${key}${value.config.typeCode.nullable ? "?" : ""}: ${value.config.typeCode.type}; `;
|
|
2249
|
+
if (value.config.omitFromFragmentType) omitKeys.push(key);
|
|
2250
|
+
}
|
|
2251
|
+
let code = omitKeys.length ? `Omit<${currentIsNullable ? `NonNullable<` : ""}${currentTypeName}${currentIsNullable ? `>` : ""}, ${omitKeys.map((k) => `"${k}"`).join(" | ")}>` : currentTypeName;
|
|
2252
|
+
if (appendCode.length) code += ` & { ${appendCode} }`;
|
|
2253
|
+
return code;
|
|
2254
|
+
}
|
|
2255
|
+
function generateFormValuesType({ formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType }) {
|
|
2256
|
+
return `type FormValues = ${generateFormValuesTypeFromTree(formValuesConfigToTree({
|
|
2257
|
+
formValuesConfig,
|
|
2258
|
+
gqlIntrospection,
|
|
2259
|
+
gqlType
|
|
2260
|
+
}), filterByFragmentType, false)};`;
|
|
2261
|
+
}
|
|
2262
|
+
function generateInitialValuesFromTree(tree, dataObject, generationType) {
|
|
2263
|
+
let code = "";
|
|
2264
|
+
for (const [key, value] of Object.entries(tree)) if (Object.keys(value.children).length > 0) {
|
|
2265
|
+
let childCode = generateInitialValuesFromTree(value.children, dataObject ? `${dataObject}.${key}` : null, generationType);
|
|
2266
|
+
if (childCode.length) if (dataObject) {
|
|
2267
|
+
childCode = `{ ...${dataObject}.${key}, ${childCode} }`;
|
|
2268
|
+
if (value.nullable) code += `${key}: ${dataObject}.${key} ? ${childCode} : undefined, `;
|
|
2269
|
+
else code += `${key}: ${childCode}, `;
|
|
2270
|
+
} else code += `${key}: { ${childCode} }, `;
|
|
2271
|
+
if (value.config?.[generationType]) throw new Error("Field has both subfields and direct initialization code, which is not supported.");
|
|
2272
|
+
} else if (value.config) {
|
|
2273
|
+
if (value.config[generationType]) code += `${key}: ${value.config[generationType]}, `;
|
|
2274
|
+
}
|
|
2275
|
+
return code;
|
|
2276
|
+
}
|
|
2277
|
+
function generateInitialValues({ mode, formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType, initialValuesAsProp }) {
|
|
2278
|
+
const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
|
|
2279
|
+
const editMode = mode === "edit" || mode == "all";
|
|
2280
|
+
const tree = formValuesConfigToTree({
|
|
2281
|
+
formValuesConfig,
|
|
2282
|
+
gqlIntrospection,
|
|
2283
|
+
gqlType
|
|
2284
|
+
});
|
|
2285
|
+
if (editMode) return `const initialValues = useMemo<Partial<FormValues>>(() => data?.${instanceGqlType}
|
|
2286
|
+
? {
|
|
2287
|
+
...filterByFragment<${filterByFragmentType}>(${instanceGqlType}FormFragment, data.${instanceGqlType}),
|
|
2288
|
+
${generateInitialValuesFromTree(tree, `data.${instanceGqlType}`, "initializationCode")}
|
|
2289
|
+
}
|
|
2290
|
+
: {
|
|
2291
|
+
${generateInitialValuesFromTree(tree, initialValuesAsProp ? `passedInitialValues` : null, "defaultInitializationCode")}
|
|
2292
|
+
${initialValuesAsProp ? `...passedInitialValues,` : ""}
|
|
2293
|
+
}
|
|
2294
|
+
, [data]);`;
|
|
2295
|
+
else return `const initialValues = {
|
|
2296
|
+
${generateInitialValuesFromTree(tree, initialValuesAsProp ? `passedInitialValues` : null, "defaultInitializationCode")}
|
|
2297
|
+
${initialValuesAsProp ? `...passedInitialValues,` : ""}
|
|
2298
|
+
};`;
|
|
2299
|
+
}
|
|
2300
|
+
function generateDestructFormValueForInputFromTree(tree, restObject) {
|
|
2301
|
+
let code = "";
|
|
2302
|
+
for (const [key, value] of Object.entries(tree)) if (Object.keys(value.children).length > 0) {
|
|
2303
|
+
const childCode = generateDestructFormValueForInputFromTree(value.children, `${restObject}${key.substring(0, 1).toUpperCase()}${key.substring(1)}`);
|
|
2304
|
+
if (childCode.length) code += `${key}: { ${childCode} }, `;
|
|
2305
|
+
} else if (value.config) {
|
|
2306
|
+
if (value.config.destructFromFormValues) code += `${key}, `;
|
|
2307
|
+
}
|
|
2308
|
+
if (code.length) {
|
|
2309
|
+
code += `...${restObject}Rest`;
|
|
2310
|
+
return code;
|
|
2311
|
+
} else return "";
|
|
2312
|
+
}
|
|
2313
|
+
function generateDestructFormValueForInput({ formValuesConfig, gqlIntrospection, gqlType }) {
|
|
2314
|
+
const code = generateDestructFormValueForInputFromTree(formValuesConfigToTree({
|
|
2315
|
+
formValuesConfig,
|
|
2316
|
+
gqlIntrospection,
|
|
2317
|
+
gqlType
|
|
2318
|
+
}), "formValues");
|
|
2319
|
+
return code.length ? `{ ${code} }` : "formValues";
|
|
2320
|
+
}
|
|
2321
|
+
function generateFormValuesToGqlInputFromTree(tree, dataObject, restObject) {
|
|
2322
|
+
let code = "";
|
|
2323
|
+
for (const [key, value] of Object.entries(tree)) if (Object.keys(value.children).length > 0) {
|
|
2324
|
+
const childRestObject = `${restObject.replace(/Rest$/, "")}${key.substring(0, 1).toUpperCase()}${key.substring(1)}Rest`;
|
|
2325
|
+
const hasChildDestruct = hasChildDestructTree(value.children);
|
|
2326
|
+
let childCode = generateFormValuesToGqlInputFromTree(value.children, hasChildDestruct ? childRestObject : `${dataObject}.${key}`, childRestObject);
|
|
2327
|
+
if (childCode.length || hasChildDestruct) {
|
|
2328
|
+
childCode = `{ ...${hasChildDestruct ? childRestObject : `${dataObject}.${key}`}, ${childCode} }`;
|
|
2329
|
+
if (value.config?.wrapFormValueToGqlInputCode) childCode = value.config.wrapFormValueToGqlInputCode.replaceAll("$fieldName", `${dataObject}.${key}`).replaceAll("$inner", childCode);
|
|
2330
|
+
code += `${key}: ${childCode}, `;
|
|
2331
|
+
}
|
|
2332
|
+
if (value.config?.formValueToGqlInputCode) throw new Error("Field has both subfields and direct formValueToGqlInputCode, which is not supported.");
|
|
2333
|
+
} else if (value.config) {
|
|
2334
|
+
if (value.config.formValueToGqlInputCode) code += `${key}: ${value.config.formValueToGqlInputCode.replaceAll("$fieldName", `${dataObject}.${key}`)}, `;
|
|
2335
|
+
}
|
|
2336
|
+
return code;
|
|
2337
|
+
}
|
|
2338
|
+
function hasChildDestructTree(tree) {
|
|
2339
|
+
return Object.values(tree).some((childValue) => {
|
|
2340
|
+
if (childValue.config?.destructFromFormValues) return true;
|
|
2341
|
+
if (Object.keys(childValue.children).length > 0) return hasChildDestructTree(childValue.children);
|
|
2342
|
+
return false;
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
function generateFormValuesToGqlInput({ formValuesConfig, gqlIntrospection, gqlType }) {
|
|
2346
|
+
const tree = formValuesConfigToTree({
|
|
2347
|
+
formValuesConfig,
|
|
2348
|
+
gqlIntrospection,
|
|
2349
|
+
gqlType
|
|
2350
|
+
});
|
|
2351
|
+
const hasChildDestruct = hasChildDestructTree(tree);
|
|
2352
|
+
const code = generateFormValuesToGqlInputFromTree(tree, hasChildDestruct ? "formValuesRest" : "formValues", hasChildDestruct ? "formValuesRest" : "formValues");
|
|
2353
|
+
if (code.length) return `const output = { ...${hasChildDestruct ? "formValuesRest" : "formValues"}, ${code} };`;
|
|
2354
|
+
else if (hasChildDestruct) return `const output = formValuesRest;`;
|
|
2355
|
+
else return `const output = formValues;`;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
//#endregion
|
|
2359
|
+
//#region src/commands/generate/generateForm/generateFragmentByFormFragmentFields.ts
|
|
2360
|
+
/**
|
|
2361
|
+
* Helper function that generates a GraphQL fragment from form fragment fields (array of dot.separated.fields).
|
|
2362
|
+
*
|
|
2363
|
+
* - Fragments are supported as "foo...FragmentName"
|
|
2364
|
+
* - for FinalFormFileUpload and FinalFormFileUploadDownloadable the needed variable is added automatically
|
|
2365
|
+
*/
|
|
2366
|
+
function generateFragmentByFormFragmentFields({ formFragmentName, gqlType, formFragmentFields }) {
|
|
2367
|
+
let fragmentCode = `
|
|
2368
|
+
fragment ${formFragmentName} on ${gqlType} {
|
|
2369
|
+
${generateGqlQueryTreeFromFields(formFragmentFields)}
|
|
2370
|
+
}
|
|
2371
|
+
`;
|
|
2372
|
+
for (const [fragmentName, fragmentVar] of Object.entries({
|
|
2373
|
+
"...FinalFormFileUpload": "${finalFormFileUploadFragment}",
|
|
2374
|
+
"...FinalFormFileUploadDownloadable": "${finalFormFileUploadDownloadableFragment}"
|
|
2375
|
+
})) if (fragmentCode.match(`${fragmentName.replace(".", "\\.")}\\b`) && !fragmentCode.includes(fragmentVar)) fragmentCode += `\n${fragmentVar}`;
|
|
2376
|
+
return fragmentCode;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
//#endregion
|
|
2380
|
+
//#region src/commands/generate/generateForm/getForwardedGqlArgs.ts
|
|
2381
|
+
function getForwardedGqlArgs$1({ fields, gqlOperation, gqlIntrospection }) {
|
|
2382
|
+
const ret = [];
|
|
2383
|
+
getArgsIncludingInputArgSubfields(gqlOperation, gqlIntrospection).forEach((arg) => {
|
|
2384
|
+
if (arg.isInputArgSubfield) {
|
|
2385
|
+
if (fields.some((field) => {
|
|
2386
|
+
return field.name === arg.name || field.name.startsWith(`${arg.name}.`);
|
|
2387
|
+
})) return;
|
|
2388
|
+
}
|
|
2389
|
+
let prop;
|
|
2390
|
+
const imports = [];
|
|
2391
|
+
if (arg.type === "ID" || arg.type === "String" || arg.type === "DateTime") prop = {
|
|
2392
|
+
name: arg.name,
|
|
2393
|
+
optional: false,
|
|
2394
|
+
type: "string"
|
|
2395
|
+
};
|
|
2396
|
+
else if (arg.type === "Boolean") prop = {
|
|
2397
|
+
name: arg.name,
|
|
2398
|
+
optional: false,
|
|
2399
|
+
type: "boolean"
|
|
2400
|
+
};
|
|
2401
|
+
else if (arg.type === "Int" || arg.type === "Float") prop = {
|
|
2402
|
+
name: arg.name,
|
|
2403
|
+
optional: false,
|
|
2404
|
+
type: "number"
|
|
2405
|
+
};
|
|
2406
|
+
else if (arg.type === "JSONObject") prop = {
|
|
2407
|
+
name: arg.name,
|
|
2408
|
+
optional: false,
|
|
2409
|
+
type: "unknown"
|
|
2410
|
+
};
|
|
2411
|
+
else {
|
|
2412
|
+
prop = {
|
|
2413
|
+
name: arg.name,
|
|
2414
|
+
optional: false,
|
|
2415
|
+
type: `GQL${arg.type}`
|
|
2416
|
+
};
|
|
2417
|
+
imports.push({
|
|
2418
|
+
name: `GQL${arg.type}`,
|
|
2419
|
+
importPath: "@src/graphql.generated"
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
ret.push({
|
|
2423
|
+
gqlArg: arg,
|
|
2424
|
+
prop,
|
|
2425
|
+
imports
|
|
2426
|
+
});
|
|
2427
|
+
});
|
|
2428
|
+
return ret;
|
|
2429
|
+
}
|
|
2430
|
+
function getArgsIncludingInputArgSubfields(gqlOperation, gqlIntrospection) {
|
|
2431
|
+
const nativeScalars = [
|
|
2432
|
+
"ID",
|
|
2433
|
+
"String",
|
|
2434
|
+
"Boolean",
|
|
2435
|
+
"Int",
|
|
2436
|
+
"Float",
|
|
2437
|
+
"DateTime",
|
|
2438
|
+
"JSONObject"
|
|
2439
|
+
];
|
|
2440
|
+
function reducer(acc, inputField) {
|
|
2441
|
+
if (inputField.type.kind !== "NON_NULL" || inputField.defaultValue) return acc;
|
|
2442
|
+
const gqlType = inputField.type.ofType;
|
|
2443
|
+
if (gqlType.kind === "INPUT_OBJECT") if (inputField.name === "input") {
|
|
2444
|
+
const typeDef = gqlIntrospection.__schema.types.find((type) => type.kind === "INPUT_OBJECT" && type.name === gqlType.name);
|
|
2445
|
+
if (typeDef && typeDef.kind === "INPUT_OBJECT") {
|
|
2446
|
+
const inputArgSubfields = typeDef.inputFields.reduce(reducer, []).map((inputArgSubfield) => {
|
|
2447
|
+
return {
|
|
2448
|
+
...inputArgSubfield,
|
|
2449
|
+
isInputArgSubfield: true
|
|
2450
|
+
};
|
|
2451
|
+
});
|
|
2452
|
+
acc.push(...inputArgSubfields);
|
|
2453
|
+
} else console.warn(`IntrospectionType for ${gqlType.name} not found or no INPUT_OBJECT`);
|
|
2454
|
+
} else acc.push({
|
|
2455
|
+
name: inputField.name,
|
|
2456
|
+
type: gqlType.name,
|
|
2457
|
+
isInputArgSubfield: false
|
|
2458
|
+
});
|
|
2459
|
+
else if (gqlType.kind === "SCALAR") if (!nativeScalars.includes(gqlType.name)) console.warn(`Currently not supported special SCALAR of type ${gqlType.name} in arg/field ${inputField.name}`);
|
|
2460
|
+
else acc.push({
|
|
2461
|
+
name: inputField.name,
|
|
2462
|
+
type: gqlType.name,
|
|
2463
|
+
isInputArgSubfield: false
|
|
2464
|
+
});
|
|
2465
|
+
else if (gqlType.kind === "ENUM") acc.push({
|
|
2466
|
+
name: inputField.name,
|
|
2467
|
+
type: gqlType.name,
|
|
2468
|
+
isInputArgSubfield: false
|
|
2469
|
+
});
|
|
2470
|
+
else if (gqlType.kind === "LIST") throw new Error(`Not supported kind ${gqlType.kind}, arg: input.${inputField.name}`);
|
|
2471
|
+
return acc;
|
|
2472
|
+
}
|
|
2473
|
+
return gqlOperation.args.reduce(reducer, []);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
//#endregion
|
|
2477
|
+
//#region src/commands/generate/generateForm/generateForm.ts
|
|
2478
|
+
/**
|
|
2479
|
+
* Detects if a mutation has a wrapped payload response type (e.g., CreateProductPayload with product and errors fields)
|
|
2480
|
+
* vs returning the entity directly.
|
|
2481
|
+
*/
|
|
2482
|
+
function hasPayloadResponseType(mutationField, gqlType) {
|
|
2483
|
+
let returnType = mutationField.type;
|
|
2484
|
+
if (returnType.kind === "NON_NULL") returnType = returnType.ofType;
|
|
2485
|
+
if (returnType.kind === "OBJECT" && returnType.name === gqlType) return false;
|
|
2486
|
+
else return true;
|
|
2487
|
+
}
|
|
2488
|
+
function generateFormPropsCode(props) {
|
|
2489
|
+
if (!props.length) return {
|
|
2490
|
+
formPropsTypeCode: "",
|
|
2491
|
+
formPropsParamsCode: ""
|
|
2492
|
+
};
|
|
2493
|
+
const uniqueProps = props.reduce((acc, item) => {
|
|
2494
|
+
const propWithSameName = acc.find((prop) => prop.name == item.name);
|
|
2495
|
+
if (!propWithSameName) return [item, ...acc];
|
|
2496
|
+
if (propWithSameName.type != item.type || propWithSameName.optional != item.optional) return [item, ...acc];
|
|
2497
|
+
return acc;
|
|
2498
|
+
}, []);
|
|
2499
|
+
return {
|
|
2500
|
+
formPropsTypeCode: `interface FormProps {
|
|
2501
|
+
${uniqueProps.map((prop) => `${prop.name}${prop.optional ? `?` : ``}: ${prop.type};`).join("\n")}
|
|
2502
|
+
}`,
|
|
2503
|
+
formPropsParamsCode: `{${uniqueProps.map((prop) => prop.name + (prop.localAliasName ? `: ${prop.localAliasName}` : "")).join(", ")}}: FormProps`
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
function generateForm({ exportName, baseOutputFilename, targetDirectory, gqlIntrospection }, config) {
|
|
2507
|
+
assertValidConfig(config);
|
|
2508
|
+
const gqlType = config.gqlType;
|
|
2509
|
+
const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
|
|
2510
|
+
const formFragmentName = config.fragmentName ?? `${gqlType}Form`;
|
|
2511
|
+
const gqlDocuments = {};
|
|
2512
|
+
const imports = [
|
|
2513
|
+
{
|
|
2514
|
+
name: "FormattedMessage",
|
|
2515
|
+
importPath: "react-intl"
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
name: "useApolloClient",
|
|
2519
|
+
importPath: "@apollo/client"
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
name: "useQuery",
|
|
2523
|
+
importPath: "@apollo/client"
|
|
2524
|
+
},
|
|
2525
|
+
{
|
|
2526
|
+
name: "gql",
|
|
2527
|
+
importPath: "@apollo/client"
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
name: "CheckboxField",
|
|
2531
|
+
importPath: "@comet/admin"
|
|
2532
|
+
},
|
|
2533
|
+
{
|
|
2534
|
+
name: "Field",
|
|
2535
|
+
importPath: "@comet/admin"
|
|
2536
|
+
},
|
|
2537
|
+
{
|
|
2538
|
+
name: "filterByFragment",
|
|
2539
|
+
importPath: "@comet/admin"
|
|
2540
|
+
},
|
|
2541
|
+
{
|
|
2542
|
+
name: "FinalForm",
|
|
2543
|
+
importPath: "@comet/admin"
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
name: "FinalFormInput",
|
|
2547
|
+
importPath: "@comet/admin"
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
name: "FinalFormRangeInput",
|
|
2551
|
+
importPath: "@comet/admin"
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
name: "FinalFormSelect",
|
|
2555
|
+
importPath: "@comet/admin"
|
|
2556
|
+
},
|
|
2557
|
+
{
|
|
2558
|
+
name: "FinalFormSubmitEvent",
|
|
2559
|
+
importPath: "@comet/admin"
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
name: "Loading",
|
|
2563
|
+
importPath: "@comet/admin"
|
|
2564
|
+
},
|
|
2565
|
+
{
|
|
2566
|
+
name: "NumberField",
|
|
2567
|
+
importPath: "@comet/admin"
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
name: "RadioGroupField",
|
|
2571
|
+
importPath: "@comet/admin"
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
name: "TextAreaField",
|
|
2575
|
+
importPath: "@comet/admin"
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
name: "TextField",
|
|
2579
|
+
importPath: "@comet/admin"
|
|
2580
|
+
},
|
|
2581
|
+
{
|
|
2582
|
+
name: "useFormApiRef",
|
|
2583
|
+
importPath: "@comet/admin"
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
name: "useStackSwitchApi",
|
|
2587
|
+
importPath: "@comet/admin"
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
name: "ArrowLeft",
|
|
2591
|
+
importPath: "@comet/admin-icons"
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
name: "Lock",
|
|
2595
|
+
importPath: "@comet/admin-icons"
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
name: "DateTimeField",
|
|
2599
|
+
importPath: "@comet/admin-date-time"
|
|
2600
|
+
},
|
|
2601
|
+
{
|
|
2602
|
+
name: "FinalFormDatePicker",
|
|
2603
|
+
importPath: "@comet/admin-date-time"
|
|
2604
|
+
},
|
|
2605
|
+
{
|
|
2606
|
+
name: "BlockState",
|
|
2607
|
+
importPath: "@comet/cms-admin"
|
|
2608
|
+
},
|
|
2609
|
+
{
|
|
2610
|
+
name: "createFinalFormBlock",
|
|
2611
|
+
importPath: "@comet/cms-admin"
|
|
2612
|
+
},
|
|
2613
|
+
{
|
|
2614
|
+
name: "queryUpdatedAt",
|
|
2615
|
+
importPath: "@comet/cms-admin"
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
name: "resolveHasSaveConflict",
|
|
2619
|
+
importPath: "@comet/cms-admin"
|
|
2620
|
+
},
|
|
2621
|
+
{
|
|
2622
|
+
name: "useFormSaveConflict",
|
|
2623
|
+
importPath: "@comet/cms-admin"
|
|
2624
|
+
},
|
|
2625
|
+
{
|
|
2626
|
+
name: "FileUploadField",
|
|
2627
|
+
importPath: "@comet/cms-admin"
|
|
2628
|
+
},
|
|
2629
|
+
{
|
|
2630
|
+
name: "IconButton",
|
|
2631
|
+
importPath: "@mui/material"
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
name: "MenuItem",
|
|
2635
|
+
importPath: "@mui/material"
|
|
2636
|
+
},
|
|
2637
|
+
{
|
|
2638
|
+
name: "InputAdornment",
|
|
2639
|
+
importPath: "@mui/material"
|
|
2640
|
+
},
|
|
2641
|
+
{
|
|
2642
|
+
name: "FormApi",
|
|
2643
|
+
importPath: "final-form"
|
|
2644
|
+
},
|
|
2645
|
+
{
|
|
2646
|
+
name: "useMemo",
|
|
2647
|
+
importPath: "react"
|
|
2648
|
+
}
|
|
2649
|
+
];
|
|
2650
|
+
const formProps = [];
|
|
2651
|
+
const mode = config.mode ?? "all";
|
|
2652
|
+
const editMode = mode === "edit" || mode == "all";
|
|
2653
|
+
const addMode = mode === "add" || mode == "all";
|
|
2654
|
+
const createMutationType = addMode && findMutationTypeOrThrow(config.createMutation ?? `create${gqlType}`, gqlIntrospection);
|
|
2655
|
+
const updateMutationType = editMode && findMutationTypeOrThrow(`update${gqlType}`, gqlIntrospection);
|
|
2656
|
+
const createMutationHasPayloadResponse = createMutationType && hasPayloadResponseType(createMutationType, gqlType);
|
|
2657
|
+
const updateMutationHasPayloadResponse = updateMutationType && hasPayloadResponseType(updateMutationType, gqlType);
|
|
2658
|
+
const errorEnums = extractErrorEnumsFromMutations({
|
|
2659
|
+
createMutationType: createMutationHasPayloadResponse ? createMutationType : null,
|
|
2660
|
+
updateMutationType: updateMutationHasPayloadResponse ? updateMutationType : null,
|
|
2661
|
+
gqlIntrospection
|
|
2662
|
+
});
|
|
2663
|
+
if (errorEnums.createErrorEnum || errorEnums.updateErrorEnum) {
|
|
2664
|
+
imports.push({
|
|
2665
|
+
name: "ReactNode",
|
|
2666
|
+
importPath: "react"
|
|
2667
|
+
});
|
|
2668
|
+
imports.push({
|
|
2669
|
+
name: "FORM_ERROR",
|
|
2670
|
+
importPath: "final-form"
|
|
2671
|
+
});
|
|
2672
|
+
imports.push({
|
|
2673
|
+
name: "FormattedMessage",
|
|
2674
|
+
importPath: "react-intl"
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
const formFields = flatFormFieldsFromFormConfig(config);
|
|
2678
|
+
let useScopeFromContext = false;
|
|
2679
|
+
const gqlArgs = [];
|
|
2680
|
+
if (createMutationType) {
|
|
2681
|
+
const forwardedArgs = getForwardedGqlArgs$1({
|
|
2682
|
+
fields: formFields,
|
|
2683
|
+
gqlOperation: createMutationType,
|
|
2684
|
+
gqlIntrospection
|
|
2685
|
+
});
|
|
2686
|
+
for (const forwardedArg of forwardedArgs) {
|
|
2687
|
+
imports.push(...forwardedArg.imports);
|
|
2688
|
+
if (forwardedArg.gqlArg.name === "scope" && !forwardedArg.gqlArg.isInputArgSubfield && !config.scopeAsProp) {
|
|
2689
|
+
useScopeFromContext = true;
|
|
2690
|
+
gqlArgs.push(forwardedArg.gqlArg);
|
|
2691
|
+
} else {
|
|
2692
|
+
formProps.push(forwardedArg.prop);
|
|
2693
|
+
gqlArgs.push(forwardedArg.gqlArg);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
if (useScopeFromContext) imports.push({
|
|
2698
|
+
name: "useContentScope",
|
|
2699
|
+
importPath: "@comet/cms-admin"
|
|
2700
|
+
});
|
|
2701
|
+
if (editMode) {
|
|
2702
|
+
if (mode === "all") formProps.push({
|
|
2703
|
+
name: "id",
|
|
2704
|
+
optional: true,
|
|
2705
|
+
type: "string"
|
|
2706
|
+
});
|
|
2707
|
+
else if (mode === "edit") formProps.push({
|
|
2708
|
+
name: "id",
|
|
2709
|
+
optional: false,
|
|
2710
|
+
type: "string"
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
const rootBlockFields = formFields.filter((field) => field.type == "block").map((field) => {
|
|
2714
|
+
if (field.type !== "block") throw new Error("Field is not a block field");
|
|
2715
|
+
return field;
|
|
2716
|
+
});
|
|
2717
|
+
rootBlockFields.forEach((field) => {
|
|
2718
|
+
if (isGeneratorConfigImport(field.block)) imports.push(convertConfigImport(field.block));
|
|
2719
|
+
});
|
|
2720
|
+
const readOnlyFields = formFields.filter((field) => field.readOnly);
|
|
2721
|
+
const fileFields = formFields.filter((field) => field.type == "fileUpload");
|
|
2722
|
+
if (fileFields.length > 0) {
|
|
2723
|
+
imports.push({
|
|
2724
|
+
name: "GQLFinalFormFileUploadFragment",
|
|
2725
|
+
importPath: "@comet/cms-admin"
|
|
2726
|
+
});
|
|
2727
|
+
imports.push({
|
|
2728
|
+
name: "GQLFinalFormFileUploadDownloadableFragment",
|
|
2729
|
+
importPath: "@comet/cms-admin"
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
let hooksCode = "";
|
|
2733
|
+
const formFragmentFields = [];
|
|
2734
|
+
const formValuesConfig = [];
|
|
2735
|
+
const generatedFields = generateFields({
|
|
2736
|
+
gqlIntrospection,
|
|
2737
|
+
baseOutputFilename,
|
|
2738
|
+
fields: config.fields,
|
|
2739
|
+
formFragmentName,
|
|
2740
|
+
formConfig: config,
|
|
2741
|
+
gqlType: config.gqlType
|
|
2742
|
+
});
|
|
2743
|
+
const fieldsCode = generatedFields.code;
|
|
2744
|
+
for (const name in generatedFields.gqlDocuments) gqlDocuments[name] = {
|
|
2745
|
+
document: generatedFields.gqlDocuments[name].document,
|
|
2746
|
+
export: true
|
|
2747
|
+
};
|
|
2748
|
+
imports.push(...generatedFields.imports);
|
|
2749
|
+
formProps.push(...generatedFields.formProps);
|
|
2750
|
+
hooksCode += generatedFields.hooksCode;
|
|
2751
|
+
formFragmentFields.push(...generatedFields.formFragmentFields);
|
|
2752
|
+
formValuesConfig.push(...generatedFields.formValuesConfig);
|
|
2753
|
+
formProps.push({
|
|
2754
|
+
name: "onCreate",
|
|
2755
|
+
optional: true,
|
|
2756
|
+
type: `(id: string) => void`
|
|
2757
|
+
});
|
|
2758
|
+
if (config.initialValuesAsProp) formProps.push({
|
|
2759
|
+
name: "initialValues",
|
|
2760
|
+
localAliasName: "passedInitialValues",
|
|
2761
|
+
optional: true,
|
|
2762
|
+
type: `Partial<FormValues>`
|
|
2763
|
+
});
|
|
2764
|
+
const { formPropsTypeCode, formPropsParamsCode } = generateFormPropsCode(formProps);
|
|
2765
|
+
gqlDocuments[`${instanceGqlType}FormFragment`] = {
|
|
2766
|
+
document: generateFragmentByFormFragmentFields({
|
|
2767
|
+
formFragmentName,
|
|
2768
|
+
gqlType,
|
|
2769
|
+
formFragmentFields
|
|
2770
|
+
}),
|
|
2771
|
+
export: editMode
|
|
2772
|
+
};
|
|
2773
|
+
if (editMode) gqlDocuments[`${instanceGqlType}Query`] = {
|
|
2774
|
+
document: generateGqlOperation({
|
|
2775
|
+
type: "query",
|
|
2776
|
+
operationName: gqlType,
|
|
2777
|
+
rootOperation: instanceGqlType,
|
|
2778
|
+
fields: [
|
|
2779
|
+
"id",
|
|
2780
|
+
"updatedAt",
|
|
2781
|
+
`...${formFragmentName}`
|
|
2782
|
+
],
|
|
2783
|
+
variables: [{
|
|
2784
|
+
name: "id",
|
|
2785
|
+
type: "ID!"
|
|
2786
|
+
}],
|
|
2787
|
+
fragmentVariables: [`\${${`${instanceGqlType}FormFragment`}}`]
|
|
2788
|
+
}),
|
|
2789
|
+
export: true
|
|
2790
|
+
};
|
|
2791
|
+
if (addMode && createMutationType) gqlDocuments[`create${gqlType}Mutation`] = {
|
|
2792
|
+
document: generateGqlOperation({
|
|
2793
|
+
type: "mutation",
|
|
2794
|
+
operationName: `Create${gqlType}`,
|
|
2795
|
+
rootOperation: createMutationType.name,
|
|
2796
|
+
fields: createMutationHasPayloadResponse ? [
|
|
2797
|
+
`${instanceGqlType}.id`,
|
|
2798
|
+
`${instanceGqlType}.updatedAt`,
|
|
2799
|
+
`${instanceGqlType}...${formFragmentName}`,
|
|
2800
|
+
"errors.code",
|
|
2801
|
+
"errors.field"
|
|
2802
|
+
] : [
|
|
2803
|
+
"id",
|
|
2804
|
+
"updatedAt",
|
|
2805
|
+
`...${formFragmentName}`
|
|
2806
|
+
],
|
|
2807
|
+
fragmentVariables: [`\${${`${instanceGqlType}FormFragment`}}`],
|
|
2808
|
+
variables: [...gqlArgs.filter((gqlArg) => !gqlArg.isInputArgSubfield).map((gqlArg) => ({
|
|
2809
|
+
name: gqlArg.name,
|
|
2810
|
+
type: `${gqlArg.type}!`
|
|
2811
|
+
})), {
|
|
2812
|
+
name: "input",
|
|
2813
|
+
type: `${gqlType}Input!`
|
|
2814
|
+
}]
|
|
2815
|
+
}),
|
|
2816
|
+
export: true
|
|
2817
|
+
};
|
|
2818
|
+
if (editMode) gqlDocuments[`update${gqlType}Mutation`] = {
|
|
2819
|
+
document: generateGqlOperation({
|
|
2820
|
+
type: "mutation",
|
|
2821
|
+
operationName: `Update${gqlType}`,
|
|
2822
|
+
rootOperation: `update${gqlType}`,
|
|
2823
|
+
fields: updateMutationHasPayloadResponse ? [
|
|
2824
|
+
`${instanceGqlType}.id`,
|
|
2825
|
+
`${instanceGqlType}.updatedAt`,
|
|
2826
|
+
`${instanceGqlType}...${formFragmentName}`,
|
|
2827
|
+
"errors.code",
|
|
2828
|
+
"errors.field"
|
|
2829
|
+
] : [
|
|
2830
|
+
"id",
|
|
2831
|
+
"updatedAt",
|
|
2832
|
+
`...${formFragmentName}`
|
|
2833
|
+
],
|
|
2834
|
+
fragmentVariables: [`\${${`${instanceGqlType}FormFragment`}}`],
|
|
2835
|
+
variables: [{
|
|
2836
|
+
name: "id",
|
|
2837
|
+
type: "ID!"
|
|
2838
|
+
}, {
|
|
2839
|
+
name: "input",
|
|
2840
|
+
type: `${gqlType}UpdateInput!`
|
|
2841
|
+
}]
|
|
2842
|
+
}),
|
|
2843
|
+
export: true
|
|
2844
|
+
};
|
|
2845
|
+
for (const name in gqlDocuments) {
|
|
2846
|
+
const gqlDocument = gqlDocuments[name];
|
|
2847
|
+
imports.push({
|
|
2848
|
+
name,
|
|
2849
|
+
importPath: `./${baseOutputFilename}.gql`
|
|
2850
|
+
});
|
|
2851
|
+
const match = gqlDocument.document.match(/^\s*(query|mutation|fragment)\s+(\w+)/);
|
|
2852
|
+
if (!match) throw new Error(`Could not find query or mutation name in ${gqlDocument}`);
|
|
2853
|
+
const type = match[1];
|
|
2854
|
+
const documentName = match[2];
|
|
2855
|
+
imports.push({
|
|
2856
|
+
name: `GQL${documentName}${type[0].toUpperCase() + type.substring(1)}`,
|
|
2857
|
+
importPath: `./${baseOutputFilename}.gql.generated`
|
|
2858
|
+
});
|
|
2859
|
+
if (type !== "fragment") imports.push({
|
|
2860
|
+
name: `GQL${documentName}${type[0].toUpperCase() + type.substring(1)}Variables`,
|
|
2861
|
+
importPath: `./${baseOutputFilename}.gql.generated`
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
const finalFormSubscription = Object.keys(generatedFields.finalFormConfig?.subscription ?? {});
|
|
2865
|
+
const finalFormRenderProps = Object.keys(generatedFields.finalFormConfig?.renderProps ?? {});
|
|
2866
|
+
let filterByFragmentType = `GQL${formFragmentName}Fragment`;
|
|
2867
|
+
let customFilterByFragment = "";
|
|
2868
|
+
if (fileFields.length > 0) {
|
|
2869
|
+
const keysToOverride = fileFields.map((field) => field.name);
|
|
2870
|
+
customFilterByFragment = `type ${formFragmentName}Fragment = Omit<${filterByFragmentType}, ${keysToOverride.map((key) => `"${String(key)}"`).join(" | ")}> & {
|
|
2871
|
+
${fileFields.map((field) => {
|
|
2872
|
+
if (field.type !== "fileUpload") throw new Error("Field is not a file upload field");
|
|
2873
|
+
if ("multiple" in field && field.multiple || "maxFiles" in field && typeof field.maxFiles === "number" && field.maxFiles > 1) return `${String(field.name)}: ${field.download ? "GQLFinalFormFileUploadDownloadableFragment" : "GQLFinalFormFileUploadFragment"}[];`;
|
|
2874
|
+
return `${String(field.name)}: ${field.download ? "GQLFinalFormFileUploadDownloadableFragment" : "GQLFinalFormFileUploadFragment"} | null;`;
|
|
2875
|
+
}).join("\n")}
|
|
2876
|
+
}`;
|
|
2877
|
+
filterByFragmentType = `${formFragmentName}Fragment`;
|
|
2878
|
+
}
|
|
2879
|
+
if (errorEnums.createErrorEnum) {
|
|
2880
|
+
const typeName = `GQL${errorEnums.createErrorEnum}`;
|
|
2881
|
+
imports.push({
|
|
2882
|
+
name: typeName,
|
|
2883
|
+
importPath: `@src/graphql.generated`
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2886
|
+
if (errorEnums.useDifferentEnums && errorEnums.updateErrorEnum) {
|
|
2887
|
+
const typeName = `GQL${errorEnums.updateErrorEnum}`;
|
|
2888
|
+
imports.push({
|
|
2889
|
+
name: typeName,
|
|
2890
|
+
importPath: `@src/graphql.generated`
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
let errorMessagesCode = "";
|
|
2894
|
+
if (errorEnums.useDifferentEnums) {
|
|
2895
|
+
if (errorEnums.createErrorEnum) {
|
|
2896
|
+
errorMessagesCode += generateErrorMessagesCode({
|
|
2897
|
+
enumName: errorEnums.createErrorEnum,
|
|
2898
|
+
gqlType,
|
|
2899
|
+
variableName: "createErrorMessages",
|
|
2900
|
+
gqlIntrospection
|
|
2901
|
+
});
|
|
2902
|
+
errorMessagesCode += "\n\n";
|
|
2903
|
+
}
|
|
2904
|
+
if (errorEnums.updateErrorEnum) errorMessagesCode += generateErrorMessagesCode({
|
|
2905
|
+
enumName: errorEnums.updateErrorEnum,
|
|
2906
|
+
gqlType,
|
|
2907
|
+
variableName: "updateErrorMessages",
|
|
2908
|
+
gqlIntrospection
|
|
2909
|
+
});
|
|
2910
|
+
} else if (errorEnums.createErrorEnum || errorEnums.updateErrorEnum) errorMessagesCode = generateErrorMessagesCode({
|
|
2911
|
+
enumName: errorEnums.createErrorEnum || errorEnums.updateErrorEnum,
|
|
2912
|
+
gqlType,
|
|
2913
|
+
variableName: "submissionErrorMessages",
|
|
2914
|
+
gqlIntrospection
|
|
2915
|
+
});
|
|
2916
|
+
return {
|
|
2917
|
+
code: `
|
|
2918
|
+
${generateImportsCode(imports)}
|
|
2919
|
+
import isEqual from "lodash.isequal";
|
|
2920
|
+
|
|
2921
|
+
${rootBlockFields.length > 0 ? `const rootBlocks = {
|
|
2922
|
+
${rootBlockFields.map((field) => `${String(field.name)}: ${field.block.name}`)}
|
|
2923
|
+
};` : ""}
|
|
2924
|
+
|
|
2925
|
+
${customFilterByFragment}
|
|
2926
|
+
|
|
2927
|
+
${generateFormValuesType({
|
|
2928
|
+
formValuesConfig,
|
|
2929
|
+
filterByFragmentType,
|
|
2930
|
+
gqlIntrospection,
|
|
2931
|
+
gqlType
|
|
2932
|
+
})}
|
|
2933
|
+
|
|
2934
|
+
${formPropsTypeCode}
|
|
2935
|
+
|
|
2936
|
+
${errorMessagesCode}
|
|
2937
|
+
|
|
2938
|
+
export function ${exportName}(${formPropsParamsCode}) {
|
|
2939
|
+
const client = useApolloClient();
|
|
2940
|
+
${mode == "all" ? `const mode = id ? "edit" : "add";` : ""}
|
|
2941
|
+
const formApiRef = useFormApiRef<FormValues>();
|
|
2942
|
+
${addMode ? `const stackSwitchApi = useStackSwitchApi();` : ""}
|
|
2943
|
+
${useScopeFromContext ? `const { scope } = useContentScope();` : ""}
|
|
2944
|
+
|
|
2945
|
+
${editMode ? `
|
|
2946
|
+
const { data, error, loading, refetch } = useQuery<GQL${gqlType}Query, GQL${gqlType}QueryVariables>(
|
|
2947
|
+
${instanceGqlType}Query,
|
|
2948
|
+
${mode == "edit" ? `{ variables: { id } }` : `id ? { variables: { id } } : { skip: true }`},
|
|
2949
|
+
);
|
|
2950
|
+
` : ""}
|
|
2951
|
+
|
|
2952
|
+
${generateInitialValues({
|
|
2953
|
+
mode,
|
|
2954
|
+
formValuesConfig,
|
|
2955
|
+
filterByFragmentType,
|
|
2956
|
+
gqlIntrospection,
|
|
2957
|
+
gqlType,
|
|
2958
|
+
initialValuesAsProp: !!config.initialValuesAsProp
|
|
2959
|
+
})}
|
|
2960
|
+
|
|
2961
|
+
|
|
2962
|
+
${editMode ? `
|
|
2963
|
+
const saveConflict = useFormSaveConflict({
|
|
2964
|
+
checkConflict: async () => {
|
|
2965
|
+
const updatedAt = await queryUpdatedAt(client, "${instanceGqlType}", id);
|
|
2966
|
+
return resolveHasSaveConflict(data?.${instanceGqlType}.updatedAt, updatedAt);
|
|
2967
|
+
},
|
|
2968
|
+
formApiRef,
|
|
2969
|
+
loadLatestVersion: async () => {
|
|
2970
|
+
await refetch();
|
|
2971
|
+
},
|
|
2972
|
+
});
|
|
2973
|
+
` : ""}
|
|
2974
|
+
|
|
2975
|
+
const handleSubmit = async (${generateDestructFormValueForInput({
|
|
2976
|
+
formValuesConfig,
|
|
2977
|
+
gqlIntrospection,
|
|
2978
|
+
gqlType
|
|
2979
|
+
})}: FormValues, form: FormApi<FormValues>${addMode ? `, event: FinalFormSubmitEvent` : ""}) => {
|
|
2980
|
+
${editMode ? `if (await saveConflict.checkForConflicts()) throw new Error("Conflicts detected");` : ""}
|
|
2981
|
+
${generateFormValuesToGqlInput({
|
|
2982
|
+
formValuesConfig,
|
|
2983
|
+
gqlIntrospection,
|
|
2984
|
+
gqlType
|
|
2985
|
+
})}
|
|
2986
|
+
|
|
2987
|
+
${mode == "all" ? `if (mode === "edit") {` : ""}
|
|
2988
|
+
${editMode ? `
|
|
2989
|
+
${readOnlyFields.some((field) => field.name === "id") ? "" : "if (!id) throw new Error();"}
|
|
2990
|
+
const { ${readOnlyFields.map((field) => `${String(field.name)},`).join("")} ...updateInput } = output;
|
|
2991
|
+
${updateMutationHasPayloadResponse ? `const { data: mutationResponse } = ` : ""}await client.mutate<GQLUpdate${gqlType}Mutation, GQLUpdate${gqlType}MutationVariables>({
|
|
2992
|
+
mutation: update${gqlType}Mutation,
|
|
2993
|
+
variables: { id, input: updateInput },
|
|
2994
|
+
});
|
|
2995
|
+
${updateMutationHasPayloadResponse && updateMutationType && errorEnums.updateErrorEnum ? `
|
|
2996
|
+
${generateErrorHandlingCode({
|
|
2997
|
+
mutationResponsePath: `mutationResponse?.${updateMutationType.name}`,
|
|
2998
|
+
errorMessagesVariable: errorEnums.useDifferentEnums ? "updateErrorMessages" : "submissionErrorMessages"
|
|
2999
|
+
})}
|
|
3000
|
+
` : ""}
|
|
3001
|
+
` : ""}
|
|
3002
|
+
${mode == "all" ? `} else {` : ""}
|
|
3003
|
+
${addMode && createMutationType ? `
|
|
3004
|
+
const { data: mutationResponse } = await client.mutate<GQLCreate${gqlType}Mutation, GQLCreate${gqlType}MutationVariables>({
|
|
3005
|
+
mutation: create${gqlType}Mutation,
|
|
3006
|
+
variables: {
|
|
3007
|
+
input: ${gqlArgs.filter((prop) => prop.isInputArgSubfield).length ? `{ ...output, ${gqlArgs.filter((prop) => prop.isInputArgSubfield).map((prop) => prop.name).join(",")} }` : "output"}${gqlArgs.filter((prop) => !prop.isInputArgSubfield).length ? `, ${gqlArgs.filter((prop) => !prop.isInputArgSubfield).map((arg) => arg.name).join(",")}` : ""} },
|
|
3008
|
+
});
|
|
3009
|
+
${createMutationHasPayloadResponse && errorEnums.createErrorEnum ? `
|
|
3010
|
+
${generateErrorHandlingCode({
|
|
3011
|
+
mutationResponsePath: `mutationResponse?.${createMutationType.name}`,
|
|
3012
|
+
errorMessagesVariable: errorEnums.useDifferentEnums ? "createErrorMessages" : "submissionErrorMessages"
|
|
3013
|
+
})}
|
|
3014
|
+
` : ""}
|
|
3015
|
+
|
|
3016
|
+
const id = mutationResponse?.${createMutationType.name}${createMutationHasPayloadResponse ? `.${instanceGqlType}?` : ""}.id;
|
|
3017
|
+
if (id) {
|
|
3018
|
+
setTimeout(() => {
|
|
3019
|
+
onCreate?.(id);
|
|
3020
|
+
${config.navigateOnCreate === true || config.navigateOnCreate === void 0 ? `if (!event.navigatingBack) { stackSwitchApi.activatePage(\`edit\`, id);` : ``}
|
|
3021
|
+
});
|
|
3022
|
+
}
|
|
3023
|
+
` : ""}
|
|
3024
|
+
${mode == "all" ? `}` : ""}
|
|
3025
|
+
};
|
|
3026
|
+
|
|
3027
|
+
${hooksCode}
|
|
3028
|
+
|
|
3029
|
+
${editMode ? ` if (error) throw error;
|
|
3030
|
+
|
|
3031
|
+
if (loading) {
|
|
3032
|
+
return <Loading behavior="fillPageHeight" />;
|
|
3033
|
+
}` : ``}
|
|
3034
|
+
|
|
3035
|
+
return (
|
|
3036
|
+
<FinalForm<FormValues>
|
|
3037
|
+
apiRef={formApiRef}
|
|
3038
|
+
onSubmit={handleSubmit}
|
|
3039
|
+
mode=${mode == "all" ? `{mode}` : editMode ? `"edit"` : `"add"`}
|
|
3040
|
+
initialValues={initialValues}
|
|
3041
|
+
initialValuesEqual={isEqual} //required to compare block data correctly
|
|
3042
|
+
subscription={{ ${finalFormSubscription.length ? finalFormSubscription.map((field) => `${field}: true`).join(", ") : ``} }}
|
|
3043
|
+
>
|
|
3044
|
+
{(${finalFormRenderProps.length ? `{${finalFormRenderProps.join(", ")}}` : ``}) => (
|
|
3045
|
+
${editMode ? `<>` : ``}
|
|
3046
|
+
${editMode ? `{saveConflict.dialogs}` : ``}
|
|
3047
|
+
<>
|
|
3048
|
+
${fieldsCode}
|
|
3049
|
+
</>
|
|
3050
|
+
${editMode ? `</>` : ``}
|
|
3051
|
+
)}
|
|
3052
|
+
</FinalForm>
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
`,
|
|
3057
|
+
gqlDocuments
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3060
|
+
/**
|
|
3061
|
+
* Checks if the provided form config is valid.
|
|
3062
|
+
*
|
|
3063
|
+
* Examples of invalid configs:
|
|
3064
|
+
* - The "id" field is not read-only
|
|
3065
|
+
*
|
|
3066
|
+
* @param config The form config to check.
|
|
3067
|
+
* @throws Will throw an error if the provided config is invalid.
|
|
3068
|
+
*/
|
|
3069
|
+
function assertValidConfig(config) {
|
|
3070
|
+
function validateFields(fields) {
|
|
3071
|
+
for (const field of fields) if (isFormFieldConfig(field)) {
|
|
3072
|
+
if (field.name === "id" && !field.readOnly) throw new Error(`Invalid form config: the "id" field must be read-only`);
|
|
3073
|
+
} else if (isFormLayoutConfig(field)) validateFields(field.fields);
|
|
3074
|
+
}
|
|
3075
|
+
validateFields(config.fields);
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
//#endregion
|
|
3079
|
+
//#region src/commands/generate/utils/findRootBlocks.ts
|
|
3080
|
+
const fallbackLibraryBlocks = {
|
|
3081
|
+
AnchorBlock: "@comet/cms-admin",
|
|
3082
|
+
DamImageBlock: "@comet/cms-admin",
|
|
3083
|
+
DamVideoBlock: "@comet/cms-admin",
|
|
3084
|
+
ExternalLinkBlock: "@comet/cms-admin",
|
|
3085
|
+
InternalLinkBlock: "@comet/cms-admin",
|
|
3086
|
+
PixelImageBlock: "@comet/cms-admin",
|
|
3087
|
+
SpaceBlock: "@comet/cms-admin",
|
|
3088
|
+
SvgImageBlock: "@comet/cms-admin",
|
|
3089
|
+
YouTubeVideoBlock: "@comet/cms-admin"
|
|
3090
|
+
};
|
|
3091
|
+
function findRootBlocks({ gqlType, targetDirectory }, schema) {
|
|
3092
|
+
const ret = {};
|
|
3093
|
+
const schemaEntity = schema.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
3094
|
+
if (!schemaEntity) throw new Error("didn't find entity in schema types");
|
|
3095
|
+
schemaEntity.fields.forEach((field) => {
|
|
3096
|
+
if (ret[field.name]) return;
|
|
3097
|
+
let type = field.type;
|
|
3098
|
+
if (type.kind == "NON_NULL") type = type.ofType;
|
|
3099
|
+
if (type.kind == "SCALAR" && type.name.endsWith("BlockData")) {
|
|
3100
|
+
let match = false;
|
|
3101
|
+
const blockName = `${type.name.replace(/BlockData$/, "")}Block`;
|
|
3102
|
+
const checkNames = [{
|
|
3103
|
+
folderName: `${targetDirectory.replace(/\/generated$/, "")}/blocks`,
|
|
3104
|
+
import: `../blocks/${blockName}`
|
|
3105
|
+
}, {
|
|
3106
|
+
folderName: `src/common/blocks`,
|
|
3107
|
+
import: `@src/common/blocks/${blockName}`
|
|
3108
|
+
}];
|
|
3109
|
+
for (const checkName of checkNames) if (existsSync(`${checkName.folderName}/${blockName}.tsx`)) {
|
|
3110
|
+
match = true;
|
|
3111
|
+
ret[field.name] = {
|
|
3112
|
+
import: checkName.import,
|
|
3113
|
+
name: blockName
|
|
3114
|
+
};
|
|
3115
|
+
break;
|
|
3116
|
+
}
|
|
3117
|
+
if (!match) {
|
|
3118
|
+
const fallback = fallbackLibraryBlocks[blockName];
|
|
3119
|
+
if (fallback) {
|
|
3120
|
+
ret[field.name] = {
|
|
3121
|
+
import: fallback,
|
|
3122
|
+
name: blockName
|
|
3123
|
+
};
|
|
3124
|
+
match = true;
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
if (!match) throw new Error(`Didn't find admin block for ${blockName} in ${checkNames.map((c) => c.folderName).join(" or ")}`);
|
|
3128
|
+
}
|
|
3129
|
+
});
|
|
3130
|
+
return ret;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
//#endregion
|
|
3134
|
+
//#region src/commands/generate/generateGrid/detectMuiXVersion.ts
|
|
3135
|
+
function detectMuiXGridVariant() {
|
|
3136
|
+
const packageJson = JSON.parse(fs.readFileSync("package.json", "utf-8"));
|
|
3137
|
+
const packages = Object.keys(packageJson.dependencies);
|
|
3138
|
+
if (packages.includes("@mui/x-data-grid-premium")) return {
|
|
3139
|
+
variant: "premium",
|
|
3140
|
+
package: "@mui/x-data-grid-premium",
|
|
3141
|
+
gridComponent: "DataGridPremium"
|
|
3142
|
+
};
|
|
3143
|
+
else if (packages.includes("@mui/x-data-grid-pro")) return {
|
|
3144
|
+
variant: "pro",
|
|
3145
|
+
package: "@mui/x-data-grid-pro",
|
|
3146
|
+
gridComponent: "DataGridPro"
|
|
3147
|
+
};
|
|
3148
|
+
else return {
|
|
3149
|
+
variant: "community",
|
|
3150
|
+
package: "@mui/x-data-grid",
|
|
3151
|
+
gridComponent: "DataGrid"
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
//#endregion
|
|
3156
|
+
//#region src/commands/generate/generateGrid/findInputObjectType.ts
|
|
3157
|
+
function findInputObjectType(input, schema) {
|
|
3158
|
+
let type = input.type;
|
|
3159
|
+
if (type.kind == "NON_NULL") type = type.ofType;
|
|
3160
|
+
if (type.kind !== "INPUT_OBJECT") throw new Error("must be INPUT_OBJECT");
|
|
3161
|
+
const typeName = type.name;
|
|
3162
|
+
return schema.__schema.types.find((type$1) => type$1.kind === "INPUT_OBJECT" && type$1.name === typeName);
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
//#endregion
|
|
3166
|
+
//#region src/commands/generate/generateGrid/generateGqlFieldList.ts
|
|
3167
|
+
const recursiveStringify = (obj) => {
|
|
3168
|
+
let ret = "";
|
|
3169
|
+
let prefixField = "";
|
|
3170
|
+
for (const key in obj) {
|
|
3171
|
+
const valueForKey = obj[key];
|
|
3172
|
+
if (typeof valueForKey === "boolean") ret += `${prefixField}${key}`;
|
|
3173
|
+
else if (typeof valueForKey === "string") ret += `${prefixField}${key}${valueForKey}`;
|
|
3174
|
+
else ret += `${prefixField}${key} { ${recursiveStringify(valueForKey)} }`;
|
|
3175
|
+
prefixField = " ";
|
|
3176
|
+
}
|
|
3177
|
+
return ret;
|
|
3178
|
+
};
|
|
3179
|
+
function generateGqlFieldList({ columns }) {
|
|
3180
|
+
return recursiveStringify(columns.reduce((acc, field) => {
|
|
3181
|
+
if (field.type === "actions") field.queryFields?.forEach((queryField) => {
|
|
3182
|
+
objectPath.set(acc, queryField, true);
|
|
3183
|
+
});
|
|
3184
|
+
else if (field.name === "id") {} else {
|
|
3185
|
+
let hasCustomFields = false;
|
|
3186
|
+
if ("labelField" in field && field.labelField) {
|
|
3187
|
+
objectPath.set(acc, `${field.name}.${field.labelField}`, true);
|
|
3188
|
+
hasCustomFields = true;
|
|
3189
|
+
}
|
|
3190
|
+
if ("queryFields" in field) {
|
|
3191
|
+
field.queryFields?.forEach((queryField) => {
|
|
3192
|
+
objectPath.set(acc, queryField, true);
|
|
3193
|
+
});
|
|
3194
|
+
hasCustomFields = true;
|
|
3195
|
+
}
|
|
3196
|
+
if (!hasCustomFields) objectPath.set(acc, field.name, true);
|
|
3197
|
+
}
|
|
3198
|
+
return acc;
|
|
3199
|
+
}, {}));
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
//#endregion
|
|
3203
|
+
//#region src/commands/generate/generateGrid/generateGridToolbar.ts
|
|
3204
|
+
const generateGridToolbar = ({ componentName, forwardToolbarAction, hasSearch, hasFilter, excelExport, allowAdding, instanceGqlType, gqlType, newEntryText, fragmentName }) => {
|
|
3205
|
+
return `${renderToolbarProps(componentName, !!forwardToolbarAction, !!excelExport)}
|
|
3206
|
+
function ${componentName}(${getGridToolbarProps(componentName, !!forwardToolbarAction, !!excelExport)}) {
|
|
3207
|
+
return (
|
|
3208
|
+
<DataGridToolbar>
|
|
3209
|
+
${hasSearch ? "<GridToolbarQuickFilter />" : ""}
|
|
3210
|
+
${hasFilter ? "<GridFilterButton />" : ""}
|
|
3211
|
+
<FillSpace />
|
|
3212
|
+
${renderToolbarActions({
|
|
3213
|
+
forwardToolbarAction,
|
|
3214
|
+
addItemText: generateFormattedMessage({
|
|
3215
|
+
config: newEntryText,
|
|
3216
|
+
id: `${instanceGqlType}.${camelCase(fragmentName)}.newEntry`,
|
|
3217
|
+
defaultMessage: `New ${camelCaseToHumanReadable(gqlType)}`,
|
|
3218
|
+
type: "jsx"
|
|
3219
|
+
}),
|
|
3220
|
+
excelExport,
|
|
3221
|
+
allowAdding
|
|
3222
|
+
})}
|
|
3223
|
+
</DataGridToolbar>
|
|
3224
|
+
);
|
|
3225
|
+
}`.replace(/^\s+\n/gm, "");
|
|
3226
|
+
};
|
|
3227
|
+
const getGridToolbarProps = (componentName, toolbarAction, exportApi) => {
|
|
3228
|
+
const props = [];
|
|
3229
|
+
if (toolbarAction) props.push({
|
|
3230
|
+
destructured: "toolbarAction",
|
|
3231
|
+
typeDefinition: "toolbarAction?: ReactNode"
|
|
3232
|
+
});
|
|
3233
|
+
if (exportApi) props.push({
|
|
3234
|
+
destructured: "exportApi",
|
|
3235
|
+
typeDefinition: "exportApi: ExportApi"
|
|
3236
|
+
});
|
|
3237
|
+
if (!props.length) return "";
|
|
3238
|
+
return `{
|
|
3239
|
+
${props.map((prop) => `${prop.destructured}`).join(",")}
|
|
3240
|
+
}: ${componentName}ToolbarProps`;
|
|
3241
|
+
};
|
|
3242
|
+
const renderToolbarActions = ({ forwardToolbarAction, addItemText, excelExport, allowAdding }) => {
|
|
3243
|
+
const showMoreActionsMenu = excelExport;
|
|
3244
|
+
if (!showMoreActionsMenu && !allowAdding) return "";
|
|
3245
|
+
const moreActionsMenu = `<CrudMoreActionsMenu
|
|
3246
|
+
slotProps={{
|
|
3247
|
+
button: {
|
|
3248
|
+
responsive: true
|
|
3249
|
+
}
|
|
3250
|
+
}}
|
|
3251
|
+
overallActions={[
|
|
3252
|
+
${excelExport ? `{
|
|
3253
|
+
label: <FormattedMessage {...messages.downloadAsExcel} />,
|
|
3254
|
+
icon: exportApi.loading ? <CircularProgress size={20} /> : <ExcelIcon />,
|
|
3255
|
+
onClick: () => exportApi.exportGrid(),
|
|
3256
|
+
disabled: exportApi.loading,
|
|
3257
|
+
}` : ""}
|
|
3258
|
+
]}
|
|
3259
|
+
/>`;
|
|
3260
|
+
const defaultAddItemButton = `<Button responsive startIcon={<AddIcon />} component={StackLink} pageName="add" payload="add">
|
|
3261
|
+
${addItemText}
|
|
3262
|
+
</Button>`;
|
|
3263
|
+
return `
|
|
3264
|
+
${showMoreActionsMenu ? moreActionsMenu : ""}
|
|
3265
|
+
${allowAdding ? forwardToolbarAction ? "{toolbarAction}" : defaultAddItemButton : ""}`;
|
|
3266
|
+
};
|
|
3267
|
+
const renderToolbarProps = (componentName, forwardToolbarAction, exportApi) => {
|
|
3268
|
+
if (forwardToolbarAction || exportApi) return `interface ${componentName}ToolbarProps extends GridToolbarProps {
|
|
3269
|
+
${forwardToolbarAction ? "toolbarAction: ReactNode;" : ""}
|
|
3270
|
+
${exportApi ? "exportApi: ExportApi;" : ""}
|
|
3271
|
+
}`;
|
|
3272
|
+
return "";
|
|
3273
|
+
};
|
|
3274
|
+
|
|
3275
|
+
//#endregion
|
|
3276
|
+
//#region src/commands/generate/generateGrid/getForwardedGqlArgs.ts
|
|
3277
|
+
function getForwardedGqlArgs(gqlFields) {
|
|
3278
|
+
const ret = [];
|
|
3279
|
+
getArgs(gqlFields, [
|
|
3280
|
+
"offset",
|
|
3281
|
+
"limit",
|
|
3282
|
+
"sort",
|
|
3283
|
+
"search",
|
|
3284
|
+
"filter",
|
|
3285
|
+
"input"
|
|
3286
|
+
]).forEach((arg) => {
|
|
3287
|
+
let prop;
|
|
3288
|
+
const imports = [];
|
|
3289
|
+
if (arg.type === "ID" || arg.type === "String" || arg.type === "DateTime") prop = {
|
|
3290
|
+
name: arg.name,
|
|
3291
|
+
optional: false,
|
|
3292
|
+
type: "string"
|
|
3293
|
+
};
|
|
3294
|
+
else if (arg.type === "Boolean") prop = {
|
|
3295
|
+
name: arg.name,
|
|
3296
|
+
optional: false,
|
|
3297
|
+
type: "boolean"
|
|
3298
|
+
};
|
|
3299
|
+
else if (arg.type === "Int" || arg.type === "Float") prop = {
|
|
3300
|
+
name: arg.name,
|
|
3301
|
+
optional: false,
|
|
3302
|
+
type: "number"
|
|
3303
|
+
};
|
|
3304
|
+
else if (arg.type === "JSONObject") prop = {
|
|
3305
|
+
name: arg.name,
|
|
3306
|
+
optional: false,
|
|
3307
|
+
type: "unknown"
|
|
3308
|
+
};
|
|
3309
|
+
else {
|
|
3310
|
+
prop = {
|
|
3311
|
+
name: arg.name,
|
|
3312
|
+
optional: false,
|
|
3313
|
+
type: `GQL${arg.type}`
|
|
3314
|
+
};
|
|
3315
|
+
imports.push({
|
|
3316
|
+
name: `GQL${arg.type}`,
|
|
3317
|
+
importPath: "@src/graphql.generated"
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
ret.push({
|
|
3321
|
+
gqlArg: {
|
|
3322
|
+
name: arg.name,
|
|
3323
|
+
type: arg.type,
|
|
3324
|
+
queryOrMutationName: arg.gqlField.name
|
|
3325
|
+
},
|
|
3326
|
+
prop,
|
|
3327
|
+
imports
|
|
3328
|
+
});
|
|
3329
|
+
});
|
|
3330
|
+
return ret;
|
|
3331
|
+
}
|
|
3332
|
+
function getArgs(gqlFields, skipGqlArgs) {
|
|
3333
|
+
return gqlFields.reduce((acc, gqlField) => {
|
|
3334
|
+
gqlField.args.forEach((gqlArg) => {
|
|
3335
|
+
if (skipGqlArgs.includes(gqlArg.name)) return acc;
|
|
3336
|
+
if (gqlArg.type.kind !== "NON_NULL" || gqlArg.defaultValue) return acc;
|
|
3337
|
+
const gqlType = gqlArg.type.ofType;
|
|
3338
|
+
let type = "unknown";
|
|
3339
|
+
if (gqlType.kind === "SCALAR") if (![
|
|
3340
|
+
"ID",
|
|
3341
|
+
"String",
|
|
3342
|
+
"Boolean",
|
|
3343
|
+
"Int",
|
|
3344
|
+
"Float",
|
|
3345
|
+
"DateTime",
|
|
3346
|
+
"JSONObject"
|
|
3347
|
+
].includes(gqlType.name)) console.warn(`Currently not supported special SCALAR of type ${gqlType.name} in param ${gqlArg.name} of ${gqlField.name}`);
|
|
3348
|
+
else type = gqlType.name;
|
|
3349
|
+
else if (gqlType.kind === "INPUT_OBJECT") type = gqlType.name;
|
|
3350
|
+
else throw new Error(`Not supported kind ${gqlType.kind}, arg: ${gqlArg.name}`);
|
|
3351
|
+
acc.push({
|
|
3352
|
+
name: gqlArg.name,
|
|
3353
|
+
type,
|
|
3354
|
+
gqlArg,
|
|
3355
|
+
gqlField
|
|
3356
|
+
});
|
|
3357
|
+
});
|
|
3358
|
+
return acc;
|
|
3359
|
+
}, []);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
//#endregion
|
|
3363
|
+
//#region src/commands/generate/generateGrid/getPropsForFilterProp.ts
|
|
3364
|
+
function getPropsForFilterProp({ config, filterType }) {
|
|
3365
|
+
if (!config.filterProp) return {
|
|
3366
|
+
hasFilterProp: false,
|
|
3367
|
+
imports: [],
|
|
3368
|
+
props: []
|
|
3369
|
+
};
|
|
3370
|
+
const filterTypeName = `GQL${filterType.name}`;
|
|
3371
|
+
return {
|
|
3372
|
+
hasFilterProp: true,
|
|
3373
|
+
imports: [{
|
|
3374
|
+
name: filterTypeName,
|
|
3375
|
+
importPath: "@src/graphql.generated"
|
|
3376
|
+
}],
|
|
3377
|
+
props: [{
|
|
3378
|
+
name: "filter",
|
|
3379
|
+
optional: true,
|
|
3380
|
+
type: filterTypeName
|
|
3381
|
+
}]
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
//#endregion
|
|
3386
|
+
//#region src/commands/generate/generateGrid/generateGrid.ts
|
|
3387
|
+
const { plural, singular } = pluralize;
|
|
3388
|
+
function tsCodeRecordToString(object, spreadAbove) {
|
|
3389
|
+
return `{${spreadAbove ? `${spreadAbove}` : ""}${Object.entries(object).filter(([key, value]) => value !== void 0).map(([key, value]) => `${key}: ${value},`).join("\n")}}`;
|
|
3390
|
+
}
|
|
3391
|
+
function generateGridPropsCode(props) {
|
|
3392
|
+
if (!props.length) return {
|
|
3393
|
+
gridPropsTypeCode: "",
|
|
3394
|
+
gridPropsParamsCode: ""
|
|
3395
|
+
};
|
|
3396
|
+
const uniqueProps = props.reduce((acc, prop) => {
|
|
3397
|
+
const propWithSameName = acc.find((filteredProps) => filteredProps.name === prop.name);
|
|
3398
|
+
if (propWithSameName) if (propWithSameName.type === prop.type) {} else throw new Error(`Prop ${prop.name} with same name but different types (${propWithSameName.type} and ${prop.type}) detected.`);
|
|
3399
|
+
else acc.push(prop);
|
|
3400
|
+
return acc;
|
|
3401
|
+
}, []);
|
|
3402
|
+
return {
|
|
3403
|
+
gridPropsTypeCode: `type Props = {
|
|
3404
|
+
${uniqueProps.map((prop) => `${prop.type.includes("any") ? `// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3405
|
+
` : ``}${prop.name}${prop.optional ? `?` : ``}: ${prop.type};`).join("\n")}
|
|
3406
|
+
};`,
|
|
3407
|
+
gridPropsParamsCode: `{${uniqueProps.map((prop) => `${prop.name} ${prop.defaultValue ? `= ${prop.defaultValue}` : ""}`).join(", ")}}: Props`
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
const getSortByValue = (sortBy) => {
|
|
3411
|
+
if (Array.isArray(sortBy)) return `[${sortBy.map((i) => `"${i}"`).join(", ")}]`;
|
|
3412
|
+
if (typeof sortBy === "string") return `"${sortBy}"`;
|
|
3413
|
+
return sortBy;
|
|
3414
|
+
};
|
|
3415
|
+
const getValueOptionsLabelData = (messageId, label) => {
|
|
3416
|
+
if (typeof label === "string" || isFormattedMessageElement(label)) return { textLabel: generateFormattedMessage({
|
|
3417
|
+
config: label,
|
|
3418
|
+
id: messageId,
|
|
3419
|
+
type: "intlCall"
|
|
3420
|
+
}) };
|
|
3421
|
+
const textLabelParts = [];
|
|
3422
|
+
const gridCellContentProps = {};
|
|
3423
|
+
if ("primaryText" in label && label.primaryText) {
|
|
3424
|
+
const primaryMessageId = `${messageId}.primary`;
|
|
3425
|
+
textLabelParts.push(generateFormattedMessage({
|
|
3426
|
+
config: label.primaryText,
|
|
3427
|
+
id: primaryMessageId,
|
|
3428
|
+
type: "intlCall"
|
|
3429
|
+
}));
|
|
3430
|
+
gridCellContentProps.primaryText = generateFormattedMessage({
|
|
3431
|
+
config: label.primaryText,
|
|
3432
|
+
id: primaryMessageId,
|
|
3433
|
+
type: "jsx"
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
if ("secondaryText" in label && label.secondaryText) {
|
|
3437
|
+
const secondaryMessageId = `${messageId}.secondary`;
|
|
3438
|
+
textLabelParts.push(generateFormattedMessage({
|
|
3439
|
+
config: label.secondaryText,
|
|
3440
|
+
id: secondaryMessageId,
|
|
3441
|
+
type: "intlCall"
|
|
3442
|
+
}));
|
|
3443
|
+
gridCellContentProps.secondaryText = generateFormattedMessage({
|
|
3444
|
+
config: label.secondaryText,
|
|
3445
|
+
id: secondaryMessageId,
|
|
3446
|
+
type: "jsx"
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
if ("icon" in label) {
|
|
3450
|
+
if (typeof label.icon === "string") gridCellContentProps.icon = `<${label.icon}Icon />`;
|
|
3451
|
+
else if (typeof label.icon === "object") if ("import" in label.icon) gridCellContentProps.icon = `<${label.icon.name} />`;
|
|
3452
|
+
else {
|
|
3453
|
+
const { name, ...iconProps } = label.icon;
|
|
3454
|
+
gridCellContentProps.icon = `<${name}Icon
|
|
3455
|
+
${Object.entries(iconProps).map(([key, value]) => `${key}="${value}"`).join("\n")}
|
|
3456
|
+
/>`;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
const gridCellContent = `<GridCellContent
|
|
3460
|
+
${Object.entries(gridCellContentProps).map(([key, value]) => `${key}={${value}}`).join("\n")}
|
|
3461
|
+
/>`;
|
|
3462
|
+
return {
|
|
3463
|
+
textLabel: textLabelParts.join(" + ' ' + "),
|
|
3464
|
+
gridCellContent
|
|
3465
|
+
};
|
|
3466
|
+
};
|
|
3467
|
+
function queryHasPaging(gridQueryType, gqlIntrospection) {
|
|
3468
|
+
let returnType = gridQueryType.type;
|
|
3469
|
+
if (returnType.kind === "NON_NULL") returnType = returnType.ofType;
|
|
3470
|
+
if (returnType.kind === "LIST") return false;
|
|
3471
|
+
if (returnType.kind === "OBJECT") {
|
|
3472
|
+
const typeName = returnType.name;
|
|
3473
|
+
const objectType = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === typeName);
|
|
3474
|
+
if (objectType) {
|
|
3475
|
+
const hasNodes = objectType.fields.some((f) => f.name === "nodes");
|
|
3476
|
+
const hasTotalCount = objectType.fields.some((f) => f.name === "totalCount");
|
|
3477
|
+
return hasNodes && hasTotalCount;
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
return false;
|
|
3481
|
+
}
|
|
3482
|
+
function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntrospection }, config) {
|
|
3483
|
+
const gqlType = config.gqlType;
|
|
3484
|
+
if (!gqlType) throw new Error("gqlType is required in grid config");
|
|
3485
|
+
const muiXGridVariant = detectMuiXGridVariant();
|
|
3486
|
+
const gqlTypePlural = plural(gqlType);
|
|
3487
|
+
const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
|
|
3488
|
+
const instanceGqlTypePlural = gqlTypePlural[0].toLowerCase() + gqlTypePlural.substring(1);
|
|
3489
|
+
const gridQuery = config.query ? config.query : instanceGqlType != instanceGqlTypePlural ? instanceGqlTypePlural : `${instanceGqlTypePlural}List`;
|
|
3490
|
+
const gqlDocuments = {};
|
|
3491
|
+
const imports = [
|
|
3492
|
+
{
|
|
3493
|
+
name: "FormattedMessage",
|
|
3494
|
+
importPath: "react-intl"
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
name: "FormattedNumber",
|
|
3498
|
+
importPath: "react-intl"
|
|
3499
|
+
},
|
|
3500
|
+
{
|
|
3501
|
+
name: "useIntl",
|
|
3502
|
+
importPath: "react-intl"
|
|
3503
|
+
},
|
|
3504
|
+
{
|
|
3505
|
+
name: "ReactNode",
|
|
3506
|
+
importPath: "react"
|
|
3507
|
+
},
|
|
3508
|
+
{
|
|
3509
|
+
name: "gql",
|
|
3510
|
+
importPath: "@apollo/client"
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
name: "useApolloClient",
|
|
3514
|
+
importPath: "@apollo/client"
|
|
3515
|
+
},
|
|
3516
|
+
{
|
|
3517
|
+
name: "useQuery",
|
|
3518
|
+
importPath: "@apollo/client"
|
|
3519
|
+
},
|
|
3520
|
+
{
|
|
3521
|
+
name: "Button",
|
|
3522
|
+
importPath: "@comet/admin"
|
|
3523
|
+
},
|
|
3524
|
+
{
|
|
3525
|
+
name: "CrudContextMenu",
|
|
3526
|
+
importPath: "@comet/admin"
|
|
3527
|
+
},
|
|
3528
|
+
{
|
|
3529
|
+
name: "CrudMoreActionsMenu",
|
|
3530
|
+
importPath: "@comet/admin"
|
|
3531
|
+
},
|
|
3532
|
+
{
|
|
3533
|
+
name: "DataGridToolbar",
|
|
3534
|
+
importPath: "@comet/admin"
|
|
3535
|
+
},
|
|
3536
|
+
{
|
|
3537
|
+
name: "ExportApi",
|
|
3538
|
+
importPath: "@comet/admin"
|
|
3539
|
+
},
|
|
3540
|
+
{
|
|
3541
|
+
name: "filterByFragment",
|
|
3542
|
+
importPath: "@comet/admin"
|
|
3543
|
+
},
|
|
3544
|
+
{
|
|
3545
|
+
name: "GridFilterButton",
|
|
3546
|
+
importPath: "@comet/admin"
|
|
3547
|
+
},
|
|
3548
|
+
{
|
|
3549
|
+
name: "GridCellContent",
|
|
3550
|
+
importPath: "@comet/admin"
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
name: "GridColDef",
|
|
3554
|
+
importPath: "@comet/admin"
|
|
3555
|
+
},
|
|
3556
|
+
{
|
|
3557
|
+
name: "dataGridDateTimeColumn",
|
|
3558
|
+
importPath: "@comet/admin"
|
|
3559
|
+
},
|
|
3560
|
+
{
|
|
3561
|
+
name: "dataGridDateColumn",
|
|
3562
|
+
importPath: "@comet/admin"
|
|
3563
|
+
},
|
|
3564
|
+
{
|
|
3565
|
+
name: "dataGridIdColumn",
|
|
3566
|
+
importPath: "@comet/admin"
|
|
3567
|
+
},
|
|
3568
|
+
{
|
|
3569
|
+
name: "dataGridManyToManyColumn",
|
|
3570
|
+
importPath: "@comet/admin"
|
|
3571
|
+
},
|
|
3572
|
+
{
|
|
3573
|
+
name: "dataGridOneToManyColumn",
|
|
3574
|
+
importPath: "@comet/admin"
|
|
3575
|
+
},
|
|
3576
|
+
{
|
|
3577
|
+
name: "renderStaticSelectCell",
|
|
3578
|
+
importPath: "@comet/admin"
|
|
3579
|
+
},
|
|
3580
|
+
{
|
|
3581
|
+
name: "messages",
|
|
3582
|
+
importPath: "@comet/admin"
|
|
3583
|
+
},
|
|
3584
|
+
{
|
|
3585
|
+
name: "muiGridFilterToGql",
|
|
3586
|
+
importPath: "@comet/admin"
|
|
3587
|
+
},
|
|
3588
|
+
{
|
|
3589
|
+
name: "StackLink",
|
|
3590
|
+
importPath: "@comet/admin"
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
name: "useStackSwitchApi",
|
|
3594
|
+
importPath: "@comet/admin"
|
|
3595
|
+
},
|
|
3596
|
+
{
|
|
3597
|
+
name: "FillSpace",
|
|
3598
|
+
importPath: "@comet/admin"
|
|
3599
|
+
},
|
|
3600
|
+
{
|
|
3601
|
+
name: "Tooltip",
|
|
3602
|
+
importPath: "@comet/admin"
|
|
3603
|
+
},
|
|
3604
|
+
{
|
|
3605
|
+
name: "useBufferedRowCount",
|
|
3606
|
+
importPath: "@comet/admin"
|
|
3607
|
+
},
|
|
3608
|
+
{
|
|
3609
|
+
name: "useDataGridExcelExport",
|
|
3610
|
+
importPath: "@comet/admin"
|
|
3611
|
+
},
|
|
3612
|
+
{
|
|
3613
|
+
name: "usePersistentColumnState",
|
|
3614
|
+
importPath: "@comet/admin"
|
|
3615
|
+
},
|
|
3616
|
+
{
|
|
3617
|
+
name: "BlockPreviewContent",
|
|
3618
|
+
importPath: "@comet/cms-admin"
|
|
3619
|
+
},
|
|
3620
|
+
{
|
|
3621
|
+
name: "Alert",
|
|
3622
|
+
importPath: "@mui/material"
|
|
3623
|
+
},
|
|
3624
|
+
{
|
|
3625
|
+
name: "Box",
|
|
3626
|
+
importPath: "@mui/material"
|
|
3627
|
+
},
|
|
3628
|
+
{
|
|
3629
|
+
name: "IconButton",
|
|
3630
|
+
importPath: "@mui/material"
|
|
3631
|
+
},
|
|
3632
|
+
{
|
|
3633
|
+
name: "Typography",
|
|
3634
|
+
importPath: "@mui/material"
|
|
3635
|
+
},
|
|
3636
|
+
{
|
|
3637
|
+
name: "useTheme",
|
|
3638
|
+
importPath: "@mui/material"
|
|
3639
|
+
},
|
|
3640
|
+
{
|
|
3641
|
+
name: "Menu",
|
|
3642
|
+
importPath: "@mui/material"
|
|
3643
|
+
},
|
|
3644
|
+
{
|
|
3645
|
+
name: "MenuItem",
|
|
3646
|
+
importPath: "@mui/material"
|
|
3647
|
+
},
|
|
3648
|
+
{
|
|
3649
|
+
name: "ListItemIcon",
|
|
3650
|
+
importPath: "@mui/material"
|
|
3651
|
+
},
|
|
3652
|
+
{
|
|
3653
|
+
name: "ListItemText",
|
|
3654
|
+
importPath: "@mui/material"
|
|
3655
|
+
},
|
|
3656
|
+
{
|
|
3657
|
+
name: "CircularProgress",
|
|
3658
|
+
importPath: "@mui/material"
|
|
3659
|
+
},
|
|
3660
|
+
{
|
|
3661
|
+
name: muiXGridVariant.gridComponent,
|
|
3662
|
+
importPath: muiXGridVariant.package
|
|
3663
|
+
},
|
|
3664
|
+
{
|
|
3665
|
+
name: `${muiXGridVariant.gridComponent}Props`,
|
|
3666
|
+
importPath: muiXGridVariant.package
|
|
3667
|
+
},
|
|
3668
|
+
{
|
|
3669
|
+
name: "GridLogicOperator",
|
|
3670
|
+
importPath: muiXGridVariant.package
|
|
3671
|
+
},
|
|
3672
|
+
{
|
|
3673
|
+
name: "GridRenderCellParams",
|
|
3674
|
+
importPath: muiXGridVariant.package
|
|
3675
|
+
},
|
|
3676
|
+
{
|
|
3677
|
+
name: "GridSlotsComponent",
|
|
3678
|
+
importPath: muiXGridVariant.package
|
|
3679
|
+
},
|
|
3680
|
+
{
|
|
3681
|
+
name: "GridToolbarProps",
|
|
3682
|
+
importPath: muiXGridVariant.package
|
|
3683
|
+
},
|
|
3684
|
+
{
|
|
3685
|
+
name: "GridColumnHeaderTitle",
|
|
3686
|
+
importPath: muiXGridVariant.package
|
|
3687
|
+
},
|
|
3688
|
+
{
|
|
3689
|
+
name: "GridToolbarQuickFilter",
|
|
3690
|
+
importPath: muiXGridVariant.package
|
|
3691
|
+
},
|
|
3692
|
+
{
|
|
3693
|
+
name: "GridRowOrderChangeParams",
|
|
3694
|
+
importPath: muiXGridVariant.package
|
|
3695
|
+
},
|
|
3696
|
+
{
|
|
3697
|
+
name: "useMemo",
|
|
3698
|
+
importPath: "react"
|
|
3699
|
+
}
|
|
3700
|
+
];
|
|
3701
|
+
const iconsToImport = [
|
|
3702
|
+
"Add",
|
|
3703
|
+
"Edit",
|
|
3704
|
+
"Info",
|
|
3705
|
+
"Excel"
|
|
3706
|
+
];
|
|
3707
|
+
const props = [];
|
|
3708
|
+
const fieldList = generateGqlFieldList({ columns: config.columns });
|
|
3709
|
+
const rootBlocks = findRootBlocks({
|
|
3710
|
+
gqlType,
|
|
3711
|
+
targetDirectory
|
|
3712
|
+
}, gqlIntrospection);
|
|
3713
|
+
config.columns.filter((column) => column.type == "block").forEach((field) => {
|
|
3714
|
+
if (rootBlocks[String(field.name)]) {
|
|
3715
|
+
const block = field.block;
|
|
3716
|
+
if (isGeneratorConfigImport(block)) {
|
|
3717
|
+
rootBlocks[String(field.name)].import = block.import;
|
|
3718
|
+
rootBlocks[String(field.name)].name = block.name;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
});
|
|
3722
|
+
Object.values(rootBlocks).forEach((block) => {
|
|
3723
|
+
if (isGeneratorConfigImport(block)) imports.push(convertConfigImport(block));
|
|
3724
|
+
});
|
|
3725
|
+
const gridQueryType = findQueryTypeOrThrow(gridQuery, gqlIntrospection);
|
|
3726
|
+
const hasPaging = queryHasPaging(gridQueryType, gqlIntrospection);
|
|
3727
|
+
if (hasPaging) imports.push({
|
|
3728
|
+
name: "useDataGridRemote",
|
|
3729
|
+
importPath: "@comet/admin"
|
|
3730
|
+
});
|
|
3731
|
+
else imports.push({
|
|
3732
|
+
name: "useDataGridUrlState",
|
|
3733
|
+
importPath: "@comet/admin"
|
|
3734
|
+
});
|
|
3735
|
+
const updateMutationType = findMutationType(`update${gqlType}`, gqlIntrospection);
|
|
3736
|
+
const hasDeleteMutation = !!findMutationType(`delete${gqlType}`, gqlIntrospection);
|
|
3737
|
+
const hasUpdateMutation = !!updateMutationType;
|
|
3738
|
+
const allowAdding = (typeof config.add === "undefined" || config.add === true) && !config.readOnly;
|
|
3739
|
+
const allowEditing = (typeof config.edit === "undefined" || config.edit === true) && !config.readOnly;
|
|
3740
|
+
const allowDeleting = (typeof config.delete === "undefined" || config.delete === true) && !config.readOnly && hasDeleteMutation;
|
|
3741
|
+
const allowRowReordering = typeof config.rowReordering?.enabled !== "undefined" && config.rowReordering?.enabled && hasUpdateMutation;
|
|
3742
|
+
const updateInputArg = updateMutationType?.args.find((arg) => arg.name === "input");
|
|
3743
|
+
if (allowRowReordering && updateInputArg) {
|
|
3744
|
+
const inputType = findInputObjectType(updateInputArg, gqlIntrospection);
|
|
3745
|
+
if (!inputType) throw new Error("Can't find update input type");
|
|
3746
|
+
if (!inputType.inputFields?.find((field) => field.name === "position")) throw new Error("Position field is needed when using 'rowReordering'");
|
|
3747
|
+
}
|
|
3748
|
+
if (allowRowReordering && typeof config.rowReordering?.dragPreviewField !== "undefined" && !config.columns.find((column) => column.type !== "actions" && column?.name === config.rowReordering?.dragPreviewField)) throw new Error(`rowReorderingOnDragField '${config.rowReordering?.dragPreviewField}' must exist in columns`);
|
|
3749
|
+
const forwardRowAction = allowEditing && config.rowActionProp;
|
|
3750
|
+
const showActionsColumn = allowEditing || allowDeleting;
|
|
3751
|
+
const showCrudContextMenuInActionsColumn = allowDeleting;
|
|
3752
|
+
const showEditInActionsColumn = allowEditing && !forwardRowAction;
|
|
3753
|
+
const defaultActionsColumnWidth = getDefaultActionsColumnWidth(showCrudContextMenuInActionsColumn, showEditInActionsColumn);
|
|
3754
|
+
let useScopeFromContext = false;
|
|
3755
|
+
const gqlArgs = [];
|
|
3756
|
+
{
|
|
3757
|
+
const forwardedArgs = getForwardedGqlArgs([gridQueryType]);
|
|
3758
|
+
for (const forwardedArg of forwardedArgs) {
|
|
3759
|
+
imports.push(...forwardedArg.imports);
|
|
3760
|
+
if (forwardedArg.gqlArg.name === "scope" && !config.scopeAsProp) {
|
|
3761
|
+
useScopeFromContext = true;
|
|
3762
|
+
gqlArgs.push(forwardedArg.gqlArg);
|
|
3763
|
+
} else {
|
|
3764
|
+
props.push(forwardedArg.prop);
|
|
3765
|
+
gqlArgs.push(forwardedArg.gqlArg);
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
if (useScopeFromContext) imports.push({
|
|
3770
|
+
name: "useContentScope",
|
|
3771
|
+
importPath: "@comet/cms-admin"
|
|
3772
|
+
});
|
|
3773
|
+
const renderToolbar = config.toolbar ?? true;
|
|
3774
|
+
const filterArg = gridQueryType.args.find((arg) => arg.name === "filter");
|
|
3775
|
+
const hasFilter = !!filterArg && renderToolbar && !allowRowReordering;
|
|
3776
|
+
let hasFilterProp = false;
|
|
3777
|
+
let filterFields = [];
|
|
3778
|
+
if (filterArg) {
|
|
3779
|
+
const filterType = findInputObjectType(filterArg, gqlIntrospection);
|
|
3780
|
+
if (!filterType) throw new Error("Can't find filter type");
|
|
3781
|
+
filterFields = filterType.inputFields.map((f) => f.name.replace(/_/g, "."));
|
|
3782
|
+
const { hasFilterProp: tempHasFilterProp, imports: filterPropImports, props: filterPropProps } = getPropsForFilterProp({
|
|
3783
|
+
config,
|
|
3784
|
+
filterType
|
|
3785
|
+
});
|
|
3786
|
+
hasFilterProp = tempHasFilterProp;
|
|
3787
|
+
imports.push(...filterPropImports);
|
|
3788
|
+
props.push(...filterPropProps);
|
|
3789
|
+
}
|
|
3790
|
+
const forwardToolbarAction = allowAdding && renderToolbar && config.toolbarActionProp;
|
|
3791
|
+
if (forwardToolbarAction) props.push({
|
|
3792
|
+
name: "toolbarAction",
|
|
3793
|
+
type: "ReactNode",
|
|
3794
|
+
optional: true
|
|
3795
|
+
});
|
|
3796
|
+
const sortArg = gridQueryType.args.find((arg) => arg.name === "sort");
|
|
3797
|
+
const hasSort = !!sortArg;
|
|
3798
|
+
let sortFields = [];
|
|
3799
|
+
if (sortArg) {
|
|
3800
|
+
imports.push({
|
|
3801
|
+
name: "muiGridSortToGql",
|
|
3802
|
+
importPath: "@comet/admin"
|
|
3803
|
+
});
|
|
3804
|
+
let sortArgType = sortArg.type;
|
|
3805
|
+
if (sortArgType.kind === "NON_NULL") sortArgType = sortArgType.ofType;
|
|
3806
|
+
if (sortArgType.kind !== "LIST") throw new Error("Sort argument must be LIST");
|
|
3807
|
+
if (sortArgType.ofType.kind !== "NON_NULL") throw new Error("Sort argument must be LIST->NON_NULL");
|
|
3808
|
+
if (sortArgType.ofType.ofType.kind !== "INPUT_OBJECT") throw new Error("Sort argument must be LIST->NON_NULL->INPUT_OBJECT");
|
|
3809
|
+
const sortTypeName = sortArgType.ofType.ofType.name;
|
|
3810
|
+
const sortType = gqlIntrospection.__schema.types.find((type) => type.kind === "INPUT_OBJECT" && type.name === sortTypeName);
|
|
3811
|
+
if (!sortType) throw new Error("Can't find sort type");
|
|
3812
|
+
const sortField = sortType.inputFields.find((i) => i.name == "field");
|
|
3813
|
+
if (!sortField) throw new Error("Can't find sortFieldName");
|
|
3814
|
+
if (sortField.type.kind !== "NON_NULL") throw new Error("sortField must be NON_NULL");
|
|
3815
|
+
if (sortField.type.ofType.kind != "ENUM") throw new Error("sortField must be NON_NULL->ENUM");
|
|
3816
|
+
const sortFieldEnumName = sortField.type.ofType.name;
|
|
3817
|
+
const sortInputEnum = gqlIntrospection.__schema.types.find((type) => type.kind === "ENUM" && type.name === sortFieldEnumName);
|
|
3818
|
+
if (!sortInputEnum) throw new Error("Can't find sortInputEnum");
|
|
3819
|
+
sortFields = sortInputEnum.enumValues.map((v) => v.name.replace(/_/g, "."));
|
|
3820
|
+
if (allowRowReordering && !sortFields.includes("position")) throw new Error("Sort argument must include 'position' field for row reordering");
|
|
3821
|
+
}
|
|
3822
|
+
const hasSearch = gridQueryType.args.some((arg) => arg.name === "search") && !allowRowReordering;
|
|
3823
|
+
const schemaEntity = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
3824
|
+
if (!schemaEntity) throw new Error("didn't find entity in schema types");
|
|
3825
|
+
const { component: actionsColumnComponent, type: actionsColumnType, headerName: actionsColumnHeaderName, pinned: actionsColumnPinned = "right", width: actionsColumnWidth = defaultActionsColumnWidth, visible: actionsColumnVisible = void 0, queryFields: actionsColumnQueryFields = [], ...restActionsColumnConfig } = config.columns.find((column) => column.type === "actions") ?? {};
|
|
3826
|
+
if (actionsColumnComponent) {
|
|
3827
|
+
if (!isGeneratorConfigImport(actionsColumnComponent)) throw new Error("Unsupported actionsColumnComponent, only imports are supported");
|
|
3828
|
+
imports.push(convertConfigImport(actionsColumnComponent));
|
|
3829
|
+
}
|
|
3830
|
+
const gridNeedsTheme = config.columns.some((column) => typeof column.visible === "string");
|
|
3831
|
+
const gridColumnFields = config.columns.filter((column) => column.type !== "actions").map((column) => {
|
|
3832
|
+
const type = column.type;
|
|
3833
|
+
const name = String(column.name);
|
|
3834
|
+
let gridColumnType = void 0;
|
|
3835
|
+
let renderCell = void 0;
|
|
3836
|
+
let valueFormatter = void 0;
|
|
3837
|
+
let valueGetter = name.includes(".") ? `(params, row) => row.${name.replace(/\./g, "?.")}` : void 0;
|
|
3838
|
+
let gridType;
|
|
3839
|
+
let filterOperators;
|
|
3840
|
+
if (column.type != "virtual" && column.filterOperators) if (isGeneratorConfigImport(column.filterOperators)) {
|
|
3841
|
+
imports.push(convertConfigImport(column.filterOperators));
|
|
3842
|
+
filterOperators = column.filterOperators.name;
|
|
3843
|
+
} else throw new Error("Unsupported filterOperators, only imports are supported for now");
|
|
3844
|
+
if (type == "dateTime") {
|
|
3845
|
+
gridColumnType = "...dataGridDateTimeColumn,";
|
|
3846
|
+
valueGetter = name.includes(".") ? `(params, row) => row.${name.replace(/\./g, "?.")} && new Date(row.${name.replace(/\./g, "?.")})` : void 0;
|
|
3847
|
+
} else if (type == "date") {
|
|
3848
|
+
gridColumnType = "...dataGridDateColumn,";
|
|
3849
|
+
valueGetter = name.includes(".") ? `(params, row) => row.${name.replace(/\./g, "?.")} && new Date(row.${name.replace(/\./g, "?.")})` : void 0;
|
|
3850
|
+
} else if (type == "number") {
|
|
3851
|
+
gridType = "number";
|
|
3852
|
+
const defaultDecimals = column.currency ? 2 : 0;
|
|
3853
|
+
const decimals = typeof column.decimals === "number" ? column.decimals : defaultDecimals;
|
|
3854
|
+
renderCell = `({ value }) => {
|
|
3855
|
+
return (typeof value === "number") ? <FormattedNumber value={value} ${column.currency ? `style="currency" currency="${column.currency}"` : ""} minimumFractionDigits={${decimals}} maximumFractionDigits={${decimals}} /> : "";
|
|
3856
|
+
}`;
|
|
3857
|
+
} else if (type == "boolean") gridType = "boolean";
|
|
3858
|
+
else if (column.type == "block") renderCell = `(params) => {
|
|
3859
|
+
return <BlockPreviewContent block={${column.block.name}} input={params.row.${name}} />;
|
|
3860
|
+
}`;
|
|
3861
|
+
else if (type == "staticSelect") {
|
|
3862
|
+
valueFormatter = `(value, row) => row.${name}?.toString()`;
|
|
3863
|
+
const introspectionField = schemaEntity.fields.find((field) => field.name === name);
|
|
3864
|
+
if (!introspectionField) throw new Error(`didn't find field ${name} in gql introspection type ${gqlType}`);
|
|
3865
|
+
const introspectionFieldType = introspectionField.type.kind === "NON_NULL" ? introspectionField.type.ofType : introspectionField.type;
|
|
3866
|
+
const enumType = gqlIntrospection.__schema.types.find((t) => t.kind === "ENUM" && t.name === introspectionFieldType.name);
|
|
3867
|
+
column.values?.forEach((value) => {
|
|
3868
|
+
if (typeof value === "object" && typeof value.label === "object" && "icon" in value.label) {
|
|
3869
|
+
if (typeof value.label.icon === "string") iconsToImport.push(value.label.icon);
|
|
3870
|
+
else if (typeof value.label.icon?.name === "string") if (isGeneratorConfigImport(value.label.icon)) imports.push(convertConfigImport(value.label.icon));
|
|
3871
|
+
else iconsToImport.push(value.label.icon.name);
|
|
3872
|
+
}
|
|
3873
|
+
});
|
|
3874
|
+
let columnValues = [];
|
|
3875
|
+
if (column.values) columnValues = column.values;
|
|
3876
|
+
else if (enumType) columnValues = enumType.enumValues.map((i) => i.name);
|
|
3877
|
+
else throw new Error(`Enum type not found`);
|
|
3878
|
+
const valueOptions = `[${columnValues.map((value) => {
|
|
3879
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return {
|
|
3880
|
+
value,
|
|
3881
|
+
label: camelCaseToHumanReadable(value.toString())
|
|
3882
|
+
};
|
|
3883
|
+
else return value;
|
|
3884
|
+
}).map(({ value, label }) => {
|
|
3885
|
+
const labelData = getValueOptionsLabelData(`${instanceGqlType}.${name}.${value.toString().charAt(0).toLowerCase() + value.toString().slice(1)}`, label);
|
|
3886
|
+
return `{
|
|
3887
|
+
value: ${JSON.stringify(value)},
|
|
3888
|
+
label: ${labelData.textLabel},
|
|
3889
|
+
${labelData.gridCellContent !== void 0 ? `cellContent: ${labelData.gridCellContent},` : ""}
|
|
3890
|
+
},`;
|
|
3891
|
+
}).join(" ")}]`;
|
|
3892
|
+
renderCell = `renderStaticSelectCell`;
|
|
3893
|
+
return {
|
|
3894
|
+
name,
|
|
3895
|
+
headerName: column.headerName,
|
|
3896
|
+
type,
|
|
3897
|
+
gridType: "singleSelect",
|
|
3898
|
+
columnType: gridColumnType,
|
|
3899
|
+
valueOptions,
|
|
3900
|
+
renderCell,
|
|
3901
|
+
valueFormatter,
|
|
3902
|
+
width: column.width,
|
|
3903
|
+
minWidth: column.minWidth,
|
|
3904
|
+
maxWidth: column.maxWidth,
|
|
3905
|
+
flex: column.flex,
|
|
3906
|
+
headerInfoTooltip: column.headerInfoTooltip,
|
|
3907
|
+
visible: column.visible !== void 0 ? typeof column.visible == "string" ? `theme.breakpoints.${column.visible}` : "false" : void 0,
|
|
3908
|
+
pinned: column.pinned,
|
|
3909
|
+
disableExport: column.disableExport
|
|
3910
|
+
};
|
|
3911
|
+
} else if (type == "id") gridColumnType = "...dataGridIdColumn,";
|
|
3912
|
+
else if (type == "manyToMany") gridColumnType = "...dataGridManyToManyColumn,";
|
|
3913
|
+
else if (type == "oneToMany") gridColumnType = "...dataGridOneToManyColumn,";
|
|
3914
|
+
if ((column.type == "text" || column.type == "number" || column.type == "boolean" || column.type == "date" || column.type == "dateTime" || column.type == "virtual" || column.type == "id" || column.type == "manyToMany" || column.type == "oneToMany") && column.renderCell) if (isGeneratorConfigCode(column.renderCell)) {
|
|
3915
|
+
renderCell = column.renderCell.code;
|
|
3916
|
+
imports.push(...column.renderCell.imports.map((imprt) => convertConfigImport(imprt)));
|
|
3917
|
+
} else throw new Error(`Unsupported renderCell for column '${name}', only arrow functions are supported`);
|
|
3918
|
+
if ((column.type === "manyToMany" || column.type === "oneToMany") && !column.renderCell) {
|
|
3919
|
+
if (!column.labelField) throw new Error(`labelField is required for ${column.type} column '${name}' if no custom renderCell is provided`);
|
|
3920
|
+
renderCell = `({ row }) => <>{row.${column.name}.map((${singular(column.name)}) => ${singular(column.name)}.${column.labelField}).join(", ")}</>`;
|
|
3921
|
+
}
|
|
3922
|
+
return {
|
|
3923
|
+
name,
|
|
3924
|
+
fieldName: column.fieldName,
|
|
3925
|
+
headerName: column.headerName,
|
|
3926
|
+
type,
|
|
3927
|
+
gridType,
|
|
3928
|
+
columnType: gridColumnType,
|
|
3929
|
+
renderCell,
|
|
3930
|
+
valueGetter,
|
|
3931
|
+
filterOperators,
|
|
3932
|
+
valueFormatter,
|
|
3933
|
+
width: column.width,
|
|
3934
|
+
minWidth: column.minWidth,
|
|
3935
|
+
maxWidth: column.maxWidth,
|
|
3936
|
+
flex: column.flex,
|
|
3937
|
+
headerInfoTooltip: column.headerInfoTooltip,
|
|
3938
|
+
visible: column.visible !== void 0 ? typeof column.visible == "string" ? `theme.breakpoints.${column.visible}` : "false" : void 0,
|
|
3939
|
+
pinned: column.pinned,
|
|
3940
|
+
disableExport: column.disableExport,
|
|
3941
|
+
sortBy: "sortBy" in column ? column.sortBy : void 0
|
|
3942
|
+
};
|
|
3943
|
+
});
|
|
3944
|
+
iconsToImport.forEach((icon) => {
|
|
3945
|
+
imports.push({
|
|
3946
|
+
name: `${icon} as ${icon}Icon`,
|
|
3947
|
+
importPath: "@comet/admin-icons"
|
|
3948
|
+
});
|
|
3949
|
+
});
|
|
3950
|
+
const fragmentName = config.fragmentName ?? `${gqlTypePlural}Form`;
|
|
3951
|
+
if (forwardRowAction) {
|
|
3952
|
+
props.push({
|
|
3953
|
+
name: "rowAction",
|
|
3954
|
+
type: `(params: GridRenderCellParams<GQL${fragmentName}Fragment>) => ReactNode`,
|
|
3955
|
+
optional: true
|
|
3956
|
+
});
|
|
3957
|
+
props.push({
|
|
3958
|
+
name: "actionsColumnWidth",
|
|
3959
|
+
type: `number`,
|
|
3960
|
+
optional: true,
|
|
3961
|
+
defaultValue: defaultActionsColumnWidth
|
|
3962
|
+
});
|
|
3963
|
+
props.push({
|
|
3964
|
+
name: "onRowClick",
|
|
3965
|
+
type: `${muiXGridVariant.gridComponent}Props["onRowClick"]`,
|
|
3966
|
+
optional: true
|
|
3967
|
+
});
|
|
3968
|
+
}
|
|
3969
|
+
if (config.selectionProps) {
|
|
3970
|
+
props.push({
|
|
3971
|
+
name: "rowSelectionModel",
|
|
3972
|
+
type: `${muiXGridVariant.gridComponent}Props["rowSelectionModel"]`,
|
|
3973
|
+
optional: true
|
|
3974
|
+
});
|
|
3975
|
+
props.push({
|
|
3976
|
+
name: "onRowSelectionModelChange",
|
|
3977
|
+
type: `${muiXGridVariant.gridComponent}Props["onRowSelectionModelChange"]`,
|
|
3978
|
+
optional: true
|
|
3979
|
+
});
|
|
3980
|
+
}
|
|
3981
|
+
const { gridPropsTypeCode, gridPropsParamsCode } = generateGridPropsCode(props);
|
|
3982
|
+
const gridToolbarComponentName = `${gqlTypePlural}GridToolbar`;
|
|
3983
|
+
const dataGridRemoteParameters = config.initialSort || config.queryParamsPrefix || config.initialFilter ? `{${config.initialSort ? ` initialSort: [${config.initialSort.map((item) => {
|
|
3984
|
+
return `{field: "${item.field}", sort: "${item.sort}"}`;
|
|
3985
|
+
}).join(",\n")} ], ` : ""}
|
|
3986
|
+
${config.initialFilter ? `initialFilter:{ ${config.initialFilter.linkOperator ? `linkOperator: GridLogicOperator.${config.initialFilter.linkOperator === "or" ? "Or" : "And"},` : ""}
|
|
3987
|
+
items: [${config.initialFilter.items.map((item) => {
|
|
3988
|
+
return `{ field: "${item.field}", operator: "${item.operator}", value: "${item.value}" }`;
|
|
3989
|
+
}).join(",\n")} ],},` : ""}
|
|
3990
|
+
${config.queryParamsPrefix ? `queryParamsPrefix: "${config.queryParamsPrefix}",` : ""}
|
|
3991
|
+
}` : "";
|
|
3992
|
+
return {
|
|
3993
|
+
code: `import {
|
|
3994
|
+
GQL${gqlTypePlural}GridQuery,
|
|
3995
|
+
GQL${gqlTypePlural}GridQueryVariables,
|
|
3996
|
+
GQL${fragmentName}Fragment,
|
|
3997
|
+
GQLCreate${gqlType}Mutation,
|
|
3998
|
+
GQLCreate${gqlType}MutationVariables,
|
|
3999
|
+
GQLUpdate${gqlType}PositionMutation,
|
|
4000
|
+
GQLUpdate${gqlType}PositionMutationVariables,
|
|
4001
|
+
GQLDelete${gqlType}Mutation,
|
|
4002
|
+
GQLDelete${gqlType}MutationVariables
|
|
4003
|
+
} from "./${baseOutputFilename}.generated";
|
|
4004
|
+
${generateImportsCode(imports)}
|
|
4005
|
+
|
|
4006
|
+
const ${instanceGqlTypePlural}Fragment = gql\`
|
|
4007
|
+
fragment ${fragmentName} on ${gqlType} {
|
|
4008
|
+
id
|
|
4009
|
+
${fieldList}
|
|
4010
|
+
}
|
|
4011
|
+
\`;
|
|
4012
|
+
|
|
4013
|
+
const ${instanceGqlTypePlural}Query = gql\`${generateGqlOperation({
|
|
4014
|
+
type: "query",
|
|
4015
|
+
operationName: `${gqlTypePlural}Grid`,
|
|
4016
|
+
rootOperation: gridQuery,
|
|
4017
|
+
fields: hasPaging ? [`nodes...${fragmentName}`, "totalCount"] : [`...${fragmentName}`],
|
|
4018
|
+
fragmentVariables: [`\${${instanceGqlTypePlural}Fragment}`],
|
|
4019
|
+
variables: [
|
|
4020
|
+
...gqlArgs.filter((gqlArg) => gqlArg.queryOrMutationName === gridQueryType.name).map((gqlArg) => ({
|
|
4021
|
+
name: gqlArg.name,
|
|
4022
|
+
type: `${gqlArg.type}!`
|
|
4023
|
+
})),
|
|
4024
|
+
...hasPaging ? [{
|
|
4025
|
+
name: "offset",
|
|
4026
|
+
type: "Int!"
|
|
4027
|
+
}, {
|
|
4028
|
+
name: "limit",
|
|
4029
|
+
type: "Int!"
|
|
4030
|
+
}] : [],
|
|
4031
|
+
...hasSort ? [{
|
|
4032
|
+
name: "sort",
|
|
4033
|
+
type: `[${gqlType}Sort!]`
|
|
4034
|
+
}] : [],
|
|
4035
|
+
...hasSearch ? [{
|
|
4036
|
+
name: "search",
|
|
4037
|
+
type: "String"
|
|
4038
|
+
}] : [],
|
|
4039
|
+
...filterArg && (hasFilter || hasFilterProp) ? [{
|
|
4040
|
+
name: "filter",
|
|
4041
|
+
type: `${gqlType}Filter`
|
|
4042
|
+
}] : []
|
|
4043
|
+
]
|
|
4044
|
+
})}\`;
|
|
4045
|
+
|
|
4046
|
+
${allowRowReordering ? `const update${gqlType}PositionMutation = gql\`
|
|
4047
|
+
mutation Update${gqlType}Position($id: ID!, $input: ${gqlType}UpdateInput!) {
|
|
4048
|
+
update${gqlType}(id: $id, input: $input) {
|
|
4049
|
+
id
|
|
4050
|
+
position
|
|
4051
|
+
updatedAt
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
\`;` : ""}
|
|
4055
|
+
|
|
4056
|
+
${allowDeleting ? `const delete${gqlType}Mutation = gql\`
|
|
4057
|
+
mutation Delete${gqlType}($id: ID!) {
|
|
4058
|
+
delete${gqlType}(id: $id)
|
|
4059
|
+
}
|
|
4060
|
+
\`;` : ""}
|
|
4061
|
+
|
|
4062
|
+
|
|
4063
|
+
${renderToolbar ? generateGridToolbar({
|
|
4064
|
+
componentName: gridToolbarComponentName,
|
|
4065
|
+
forwardToolbarAction,
|
|
4066
|
+
hasSearch,
|
|
4067
|
+
hasFilter,
|
|
4068
|
+
allowAdding,
|
|
4069
|
+
instanceGqlType,
|
|
4070
|
+
gqlType,
|
|
4071
|
+
excelExport: config.excelExport,
|
|
4072
|
+
newEntryText: config.newEntryText,
|
|
4073
|
+
fragmentName
|
|
4074
|
+
}) : ""}
|
|
4075
|
+
|
|
4076
|
+
${gridPropsTypeCode}
|
|
4077
|
+
|
|
4078
|
+
export function ${gqlTypePlural}Grid(${gridPropsParamsCode}) {
|
|
4079
|
+
${showCrudContextMenuInActionsColumn ? "const client = useApolloClient();" : ""}
|
|
4080
|
+
const intl = useIntl();
|
|
4081
|
+
const dataGridProps = { ...${hasPaging ? "useDataGridRemote" : "useDataGridUrlState"}(${dataGridRemoteParameters}), ...usePersistentColumnState("${gqlTypePlural}Grid")${config.selectionProps === "multiSelect" ? `, rowSelectionModel, onRowSelectionModelChange, checkboxSelection: true, keepNonExistentRowsSelected: true` : config.selectionProps === "singleSelect" ? `, rowSelectionModel, onRowSelectionModelChange, checkboxSelection: false, keepNonExistentRowsSelected: false, disableRowSelectionOnClick: false` : ``} };
|
|
4082
|
+
${useScopeFromContext ? `const { scope } = useContentScope();` : ""}
|
|
4083
|
+
${gridNeedsTheme ? `const theme = useTheme();` : ""}
|
|
4084
|
+
${showEditInActionsColumn ? `const stackSwitchApi = useStackSwitchApi();` : ""}
|
|
4085
|
+
|
|
4086
|
+
${generateHandleRowOrderChange(allowRowReordering, gqlType, instanceGqlTypePlural)}
|
|
4087
|
+
|
|
4088
|
+
${showEditInActionsColumn ? `const handleRowClick: ${muiXGridVariant.gridComponent}Props["onRowClick"] = (params) => {
|
|
4089
|
+
stackSwitchApi.activatePage("edit", params.row.id);
|
|
4090
|
+
};` : ""}
|
|
4091
|
+
|
|
4092
|
+
const columns: GridColDef<GQL${fragmentName}Fragment>[] = useMemo(()=>[
|
|
4093
|
+
${gridColumnFields.map((column) => {
|
|
4094
|
+
const defaultMinWidth = 150;
|
|
4095
|
+
const defaultColumnFlex = 1;
|
|
4096
|
+
let minWidth;
|
|
4097
|
+
let maxWidth;
|
|
4098
|
+
let tooltipColumnWidth = column.width;
|
|
4099
|
+
if (typeof column.width === "undefined") {
|
|
4100
|
+
maxWidth = column.maxWidth;
|
|
4101
|
+
if (typeof column.minWidth === "undefined" && (typeof column.maxWidth === "undefined" || column.maxWidth >= defaultMinWidth)) {
|
|
4102
|
+
minWidth = defaultMinWidth;
|
|
4103
|
+
tooltipColumnWidth = defaultMinWidth;
|
|
4104
|
+
} else if (typeof column.minWidth !== "undefined") {
|
|
4105
|
+
minWidth = column.minWidth;
|
|
4106
|
+
tooltipColumnWidth = column.minWidth;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
let isColumnFilterable = !hasPaging ? true : filterFields.includes(column.name) || !!column.filterOperators;
|
|
4110
|
+
if (allowRowReordering) isColumnFilterable = false;
|
|
4111
|
+
if (column.type == "block" && !hasPaging) isColumnFilterable = false;
|
|
4112
|
+
let isColumnSortable = !hasPaging ? true : sortFields.includes(column.name) || !!column.sortBy;
|
|
4113
|
+
if (allowRowReordering) isColumnSortable = false;
|
|
4114
|
+
if (column.type == "block" && !hasPaging) isColumnSortable = false;
|
|
4115
|
+
const columnDefinition = {
|
|
4116
|
+
field: column.fieldName ? `"${column.fieldName}"` : `"${column.name.replace(/\./g, "_")}"`,
|
|
4117
|
+
renderHeader: column.headerInfoTooltip ? `() => (
|
|
4118
|
+
<>
|
|
4119
|
+
<GridColumnHeaderTitle label={${generateFormattedMessage({
|
|
4120
|
+
config: column.headerName,
|
|
4121
|
+
id: `${instanceGqlType}.${column.name}`,
|
|
4122
|
+
defaultMessage: camelCaseToHumanReadable(column.name),
|
|
4123
|
+
type: "intlCall"
|
|
4124
|
+
})}} columnWidth= {${tooltipColumnWidth}}
|
|
4125
|
+
/>
|
|
4126
|
+
<Tooltip
|
|
4127
|
+
title={${generateFormattedMessage({
|
|
4128
|
+
config: column.headerInfoTooltip,
|
|
4129
|
+
id: `${instanceGqlType}.${column.name}.tooltip`,
|
|
4130
|
+
type: "jsx"
|
|
4131
|
+
})}}
|
|
4132
|
+
>
|
|
4133
|
+
<InfoIcon sx={{ marginLeft: 1 }} />
|
|
4134
|
+
</Tooltip>
|
|
4135
|
+
</>
|
|
4136
|
+
)` : void 0,
|
|
4137
|
+
headerName: column.headerName === "" ? `""` : generateFormattedMessage({
|
|
4138
|
+
config: column.headerName,
|
|
4139
|
+
id: `${instanceGqlType}.${column.name}`,
|
|
4140
|
+
defaultMessage: camelCaseToHumanReadable(column.name),
|
|
4141
|
+
type: "intlCall"
|
|
4142
|
+
}),
|
|
4143
|
+
type: column.gridType ? `"${column.gridType}"` : void 0,
|
|
4144
|
+
filterable: isColumnFilterable ? void 0 : `false`,
|
|
4145
|
+
sortable: isColumnSortable ? void 0 : `false`,
|
|
4146
|
+
valueGetter: column.valueGetter,
|
|
4147
|
+
valueFormatter: column.valueFormatter,
|
|
4148
|
+
valueOptions: column.valueOptions,
|
|
4149
|
+
renderCell: column.renderCell,
|
|
4150
|
+
filterOperators: column.filterOperators,
|
|
4151
|
+
width: column.width,
|
|
4152
|
+
flex: column.flex,
|
|
4153
|
+
pinned: column.pinned && `"${column.pinned}"`,
|
|
4154
|
+
visible: column.visible,
|
|
4155
|
+
disableExport: column.disableExport ? "true" : void 0
|
|
4156
|
+
};
|
|
4157
|
+
if ("sortBy" in column && column.sortBy) columnDefinition["sortBy"] = getSortByValue(column.sortBy);
|
|
4158
|
+
if (typeof column.width === "undefined") {
|
|
4159
|
+
columnDefinition.flex = defaultColumnFlex;
|
|
4160
|
+
columnDefinition.minWidth = minWidth;
|
|
4161
|
+
columnDefinition.maxWidth = maxWidth;
|
|
4162
|
+
}
|
|
4163
|
+
return tsCodeRecordToString(columnDefinition, column.columnType);
|
|
4164
|
+
}).join(",\n")},
|
|
4165
|
+
${showActionsColumn ? tsCodeRecordToString({
|
|
4166
|
+
field: "\"actions\"",
|
|
4167
|
+
headerName: actionsColumnHeaderName ? generateFormattedMessage({
|
|
4168
|
+
config: actionsColumnHeaderName,
|
|
4169
|
+
id: `${instanceGqlType}.actions`,
|
|
4170
|
+
type: "intlCall"
|
|
4171
|
+
}) : `""`,
|
|
4172
|
+
sortable: "false",
|
|
4173
|
+
filterable: "false",
|
|
4174
|
+
type: "\"actions\"",
|
|
4175
|
+
align: "\"right\"",
|
|
4176
|
+
pinned: `"${actionsColumnPinned}"`,
|
|
4177
|
+
width: forwardRowAction ? "actionsColumnWidth" : actionsColumnWidth,
|
|
4178
|
+
visible: actionsColumnVisible !== void 0 ? typeof actionsColumnVisible == "string" ? `theme.breakpoints.${actionsColumnVisible}` : "false" : void 0,
|
|
4179
|
+
...restActionsColumnConfig,
|
|
4180
|
+
headerInfoTooltip: restActionsColumnConfig.headerInfoTooltip ? generateFormattedMessage({
|
|
4181
|
+
config: restActionsColumnConfig.headerInfoTooltip,
|
|
4182
|
+
id: `${instanceGqlType}.actions`,
|
|
4183
|
+
type: "intlCall"
|
|
4184
|
+
}) : void 0,
|
|
4185
|
+
disableExport: config.excelExport ? "true" : void 0,
|
|
4186
|
+
renderCell: `(params) => {
|
|
4187
|
+
return (
|
|
4188
|
+
<>
|
|
4189
|
+
${actionsColumnComponent?.name ? `<${actionsColumnComponent.name} {...params} />` : ""}${allowEditing ? forwardRowAction ? `{rowAction && rowAction(params)}` : `
|
|
4190
|
+
<IconButton color="primary" component={StackLink} pageName="edit" payload={params.row.id}>
|
|
4191
|
+
<EditIcon />
|
|
4192
|
+
</IconButton>` : ""}${showCrudContextMenuInActionsColumn ? `
|
|
4193
|
+
<CrudContextMenu
|
|
4194
|
+
${allowDeleting ? `
|
|
4195
|
+
onDelete={async () => {
|
|
4196
|
+
await client.mutate<GQLDelete${gqlType}Mutation, GQLDelete${gqlType}MutationVariables>({
|
|
4197
|
+
mutation: delete${gqlType}Mutation,
|
|
4198
|
+
variables: { id: params.row.id },
|
|
4199
|
+
});
|
|
4200
|
+
}}
|
|
4201
|
+
` : ""}
|
|
4202
|
+
refetchQueries={[${instanceGqlTypePlural}Query]}
|
|
4203
|
+
${config.crudContextMenu?.deleteType ? `deleteType="${config.crudContextMenu.deleteType}"` : ""}
|
|
4204
|
+
${config.crudContextMenu?.deleteText ? `messagesMapping={{ delete: <FormattedMessage id="${instanceGqlType}.crudContextMenu.delete" defaultMessage="${config.crudContextMenu.deleteText}" /> }}` : ""}
|
|
4205
|
+
/>
|
|
4206
|
+
` : ""}
|
|
4207
|
+
</>
|
|
4208
|
+
);
|
|
4209
|
+
}`
|
|
4210
|
+
}) : ""}
|
|
4211
|
+
],[intl${gridNeedsTheme ? ", theme" : ""}${showCrudContextMenuInActionsColumn ? ", client" : ""}${forwardRowAction ? ", rowAction, actionsColumnWidth" : ""}]);
|
|
4212
|
+
|
|
4213
|
+
${hasFilter || hasSearch ? `const { ${hasFilter ? `filter: gqlFilter, ` : ""}${hasSearch ? `search: gqlSearch, ` : ""} } = muiGridFilterToGql(columns, dataGridProps.filterModel);` : ""}
|
|
4214
|
+
|
|
4215
|
+
const { data, loading, error } = useQuery<GQL${gqlTypePlural}GridQuery, GQL${gqlTypePlural}GridQueryVariables>(${instanceGqlTypePlural}Query, {
|
|
4216
|
+
variables: {
|
|
4217
|
+
${[
|
|
4218
|
+
...gqlArgs.filter((gqlArg) => gqlArg.queryOrMutationName === gridQueryType.name).map((arg) => arg.name),
|
|
4219
|
+
...filterArg ? hasFilter && hasFilterProp ? ["filter: filter ? { and: [gqlFilter, filter] } : gqlFilter"] : hasFilter ? ["filter: gqlFilter"] : hasFilterProp ? ["filter"] : [] : [],
|
|
4220
|
+
...hasSearch ? ["search: gqlSearch"] : [],
|
|
4221
|
+
...hasSort ? !allowRowReordering ? ["sort: muiGridSortToGql(dataGridProps.sortModel, columns)"] : [`sort: { field: "position", direction: "ASC" }`] : [],
|
|
4222
|
+
...hasPaging ? !allowRowReordering ? [`offset: dataGridProps.paginationModel.page * dataGridProps.paginationModel.pageSize`, `limit: dataGridProps.paginationModel.pageSize`] : [`offset: 0`, `limit: 100`] : []
|
|
4223
|
+
].join(", ")}
|
|
4224
|
+
},
|
|
4225
|
+
});
|
|
4226
|
+
${hasPaging ? `const rowCount = useBufferedRowCount(data?.${gridQuery}.totalCount);` : ""}
|
|
4227
|
+
if (error) throw error;
|
|
4228
|
+
const rows = ${!allowRowReordering ? hasPaging ? `data?.${gridQuery}.nodes` : `data?.${gridQuery}` : generateRowReorderingRows(gridQuery, fieldList, config.rowReordering?.dragPreviewField)} ?? [];
|
|
4229
|
+
|
|
4230
|
+
${generateGridExportApi(config.excelExport, gqlTypePlural, instanceGqlTypePlural, gridQuery, gqlArgs, hasPaging)}
|
|
4231
|
+
|
|
4232
|
+
return (
|
|
4233
|
+
<${muiXGridVariant.gridComponent}
|
|
4234
|
+
{...dataGridProps}
|
|
4235
|
+
rows={rows}
|
|
4236
|
+
${hasPaging ? `rowCount={rowCount}` : ""}
|
|
4237
|
+
columns={columns}
|
|
4238
|
+
loading={loading}
|
|
4239
|
+
${renderToolbar ? `slots={{
|
|
4240
|
+
toolbar: ${gridToolbarComponentName} as GridSlotsComponent["toolbar"],
|
|
4241
|
+
}}
|
|
4242
|
+
${getDataGridSlotProps(gridToolbarComponentName, forwardToolbarAction, config.excelExport)}` : ""}
|
|
4243
|
+
${allowRowReordering ? `rowReordering
|
|
4244
|
+
onRowOrderChange={handleRowOrderChange}
|
|
4245
|
+
hideFooterPagination` : ""}
|
|
4246
|
+
${config.density ? `density="${config.density}"` : ""}
|
|
4247
|
+
${showEditInActionsColumn ? `onRowClick={handleRowClick}` : forwardRowAction ? `onRowClick={onRowClick}` : ""}
|
|
4248
|
+
/>
|
|
4249
|
+
);
|
|
4250
|
+
}
|
|
4251
|
+
`,
|
|
4252
|
+
gqlDocuments
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
4255
|
+
const generateRowReorderingRows = (gridQuery, fieldList, dragPreviewField) => {
|
|
4256
|
+
const fields = fieldList.split(" ");
|
|
4257
|
+
return `data?.${gridQuery}.nodes.map((node) => ({
|
|
4258
|
+
...node,
|
|
4259
|
+
__reorder__: node.${dragPreviewField || fields.find((field) => field === "title" || field === "name") || "id"}
|
|
4260
|
+
}))`;
|
|
4261
|
+
};
|
|
4262
|
+
const getDefaultActionsColumnWidth = (showCrudContextMenu, showEdit) => {
|
|
4263
|
+
let numberOfActions = 0;
|
|
4264
|
+
if (showCrudContextMenu) numberOfActions += 1;
|
|
4265
|
+
if (showEdit) numberOfActions += 1;
|
|
4266
|
+
return numberOfActions * 32 + 20;
|
|
4267
|
+
};
|
|
4268
|
+
const getDataGridSlotProps = (componentName, forwardToolbarAction, excelExport) => {
|
|
4269
|
+
if (!forwardToolbarAction && !excelExport) return "";
|
|
4270
|
+
return `slotProps={{
|
|
4271
|
+
toolbar: ${`{
|
|
4272
|
+
${forwardToolbarAction ? `toolbarAction,` : ""}
|
|
4273
|
+
${excelExport ? `exportApi,` : ""}
|
|
4274
|
+
} as ${componentName}ToolbarProps`.replace(/\n/g, "")}
|
|
4275
|
+
}}`;
|
|
4276
|
+
};
|
|
4277
|
+
const generateGridExportApi = (excelExport, gqlTypePlural, instanceGqlTypePlural, gridQuery, gqlArgs, hasPaging) => {
|
|
4278
|
+
if (!excelExport) return "";
|
|
4279
|
+
const queryNodeType = hasPaging ? `GQL${gqlTypePlural}GridQuery["${gridQuery}"]["nodes"][0]` : `GQL${gqlTypePlural}GridQuery["${gridQuery}"][0]`;
|
|
4280
|
+
const variablesType = hasPaging ? `Omit<GQL${gqlTypePlural}GridQueryVariables, "offset" | "limit">` : `GQL${gqlTypePlural}GridQueryVariables`;
|
|
4281
|
+
const resolveQueryNodes = hasPaging ? `(data) => data.${gridQuery}.nodes` : `(data) => data.${gridQuery}`;
|
|
4282
|
+
const totalCount = hasPaging ? `data?.${gridQuery}.totalCount ?? 0` : `data?.${gridQuery}.length ?? 0`;
|
|
4283
|
+
return `const exportApi = useDataGridExcelExport<${queryNodeType}, GQL${gqlTypePlural}GridQuery, ${variablesType}>({
|
|
4284
|
+
columns,
|
|
4285
|
+
variables: {
|
|
4286
|
+
${[...gqlArgs.filter((gqlArg) => gqlArg.queryOrMutationName === gridQuery).map((arg) => arg.name), `...muiGridFilterToGql(columns, dataGridProps.filterModel)`].join(", ")}
|
|
4287
|
+
},
|
|
4288
|
+
query: ${instanceGqlTypePlural}Query,
|
|
4289
|
+
resolveQueryNodes: ${resolveQueryNodes},
|
|
4290
|
+
totalCount: ${totalCount},
|
|
4291
|
+
${!hasPaging ? `hasPaging: false,` : ""}
|
|
4292
|
+
exportOptions: {
|
|
4293
|
+
fileName: "${gqlTypePlural}",
|
|
4294
|
+
},
|
|
4295
|
+
});`;
|
|
4296
|
+
};
|
|
4297
|
+
const generateHandleRowOrderChange = (allowRowReordering, gqlType, instanceGqlTypePlural) => {
|
|
4298
|
+
if (!allowRowReordering) return "";
|
|
4299
|
+
return `const handleRowOrderChange = async ({ row: { id }, targetIndex }: GridRowOrderChangeParams) => {
|
|
4300
|
+
await client.mutate<GQLUpdate${gqlType}PositionMutation, GQLUpdate${gqlType}PositionMutationVariables>({
|
|
4301
|
+
mutation: update${gqlType}PositionMutation,
|
|
4302
|
+
variables: { id, input: { position: targetIndex + 1 } },
|
|
4303
|
+
awaitRefetchQueries: true,
|
|
4304
|
+
refetchQueries: [${instanceGqlTypePlural}Query]
|
|
4305
|
+
});
|
|
4306
|
+
};`;
|
|
4307
|
+
};
|
|
4308
|
+
|
|
4309
|
+
//#endregion
|
|
4310
|
+
//#region src/commands/generate/utils/writeGenerated.ts
|
|
4311
|
+
async function writeGenerated(filePath, contents) {
|
|
4312
|
+
const header = `// This file has been generated by comet admin-generator.
|
|
4313
|
+
// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment.
|
|
4314
|
+
`;
|
|
4315
|
+
await promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
4316
|
+
const sourceFile = ts.createSourceFile(filePath, header + contents, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
4317
|
+
const result = ts.transform(sourceFile, [removeUnusedImports()]);
|
|
4318
|
+
const prettyCode = ts.createPrinter({
|
|
4319
|
+
newLine: ts.NewLineKind.LineFeed,
|
|
4320
|
+
removeComments: false
|
|
4321
|
+
}).printFile(result.transformed[0]).replace(/gql `/g, "gql`");
|
|
4322
|
+
await promises.writeFile(filePath, prettyCode);
|
|
4323
|
+
console.log(`generated ${filePath}`);
|
|
4324
|
+
}
|
|
4325
|
+
function removeUnusedImports() {
|
|
4326
|
+
return (context) => {
|
|
4327
|
+
function visit(sourceFile) {
|
|
4328
|
+
const usedIdentifiers = /* @__PURE__ */ new Set();
|
|
4329
|
+
function collectUsage(node) {
|
|
4330
|
+
if (ts.isIdentifier(node)) usedIdentifiers.add(node.text);
|
|
4331
|
+
else if (ts.isImportDeclaration(node)) return;
|
|
4332
|
+
ts.forEachChild(node, collectUsage);
|
|
4333
|
+
}
|
|
4334
|
+
ts.forEachChild(sourceFile, collectUsage);
|
|
4335
|
+
function visitor(node) {
|
|
4336
|
+
if (ts.isImportDeclaration(node) && node.importClause) {
|
|
4337
|
+
const { name, namedBindings } = node.importClause;
|
|
4338
|
+
const updatedBindings = [];
|
|
4339
|
+
if (name && usedIdentifiers.has(name.text)) {} else if (name) return;
|
|
4340
|
+
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
4341
|
+
for (const specifier of namedBindings.elements) {
|
|
4342
|
+
const importName = specifier.name.text;
|
|
4343
|
+
if (usedIdentifiers.has(importName)) updatedBindings.push(specifier);
|
|
4344
|
+
}
|
|
4345
|
+
if (updatedBindings.length === 0 && !name) return;
|
|
4346
|
+
return ts.factory.updateImportDeclaration(node, node.modifiers, ts.factory.updateImportClause(node.importClause, node.importClause.isTypeOnly, name, updatedBindings.length > 0 ? ts.factory.updateNamedImports(namedBindings, updatedBindings) : void 0), node.moduleSpecifier, node.assertClause);
|
|
4347
|
+
}
|
|
4348
|
+
if (!namedBindings && !name) return node;
|
|
4349
|
+
return node;
|
|
4350
|
+
}
|
|
4351
|
+
return ts.visitEachChild(node, visitor, context);
|
|
4352
|
+
}
|
|
4353
|
+
return ts.visitNode(sourceFile, visitor);
|
|
4354
|
+
}
|
|
4355
|
+
return (sourceFile) => visit(sourceFile);
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
//#endregion
|
|
4360
|
+
//#region src/commands/generate/generate-command.ts
|
|
4361
|
+
const exec$1 = promisify(exec);
|
|
4362
|
+
function isComponentFormFieldConfig(arg) {
|
|
4363
|
+
return arg && arg.type === "component";
|
|
4364
|
+
}
|
|
4365
|
+
function isFormFieldConfig(arg) {
|
|
4366
|
+
return !isFormLayoutConfig(arg) && !isComponentFormFieldConfig(arg);
|
|
4367
|
+
}
|
|
4368
|
+
function isFormLayoutConfig(arg) {
|
|
4369
|
+
return arg.type !== void 0 && ["fieldSet", "optionalNestedFields"].includes(arg.type);
|
|
4370
|
+
}
|
|
4371
|
+
function injectFormVariables(fn) {
|
|
4372
|
+
return fn({});
|
|
4373
|
+
}
|
|
4374
|
+
function defineConfig(config) {
|
|
4375
|
+
return config;
|
|
4376
|
+
}
|
|
4377
|
+
/**
|
|
4378
|
+
* @experimental
|
|
4379
|
+
*/
|
|
4380
|
+
async function runGenerate(filePattern = "src/**/*.cometGen.{ts,tsx}") {
|
|
4381
|
+
const gqlIntrospection = introspectionFromSchema(await loadSchema("./schema.gql", { loaders: [new GraphQLFileLoader()] }));
|
|
4382
|
+
const writtenFiles = [];
|
|
4383
|
+
const files = await glob(filePattern);
|
|
4384
|
+
for (const file of files) {
|
|
4385
|
+
let outputCode = "";
|
|
4386
|
+
let gqlDocumentsOutputCode = "";
|
|
4387
|
+
const targetDirectory = `${dirname(file)}/generated`;
|
|
4388
|
+
const baseOutputFilename = basename(file).replace(/\.cometGen\.tsx?$/, "");
|
|
4389
|
+
console.log(`generating ${file}`);
|
|
4390
|
+
const config = await parseConfig(`${process.cwd()}/${file}`);
|
|
4391
|
+
const codeOuputFilename = `${targetDirectory}/${basename(file.replace(/\.cometGen\.tsx?$/, ""))}.tsx`;
|
|
4392
|
+
await promises.rm(codeOuputFilename, { force: true });
|
|
4393
|
+
const exportName = file.match(/([^/]+)\.cometGen\.tsx?$/)?.[1];
|
|
4394
|
+
if (!exportName) throw new Error("Can not determine exportName");
|
|
4395
|
+
let generated;
|
|
4396
|
+
if (config.type == "form") generated = generateForm({
|
|
4397
|
+
exportName,
|
|
4398
|
+
gqlIntrospection,
|
|
4399
|
+
baseOutputFilename,
|
|
4400
|
+
targetDirectory
|
|
4401
|
+
}, config);
|
|
4402
|
+
else if (config.type == "grid") generated = generateGrid({
|
|
4403
|
+
exportName,
|
|
4404
|
+
gqlIntrospection,
|
|
4405
|
+
baseOutputFilename,
|
|
4406
|
+
targetDirectory
|
|
4407
|
+
}, config);
|
|
4408
|
+
else throw new Error(`Unknown config type`);
|
|
4409
|
+
outputCode += generated.code;
|
|
4410
|
+
for (const queryName in generated.gqlDocuments) {
|
|
4411
|
+
const exportStatement = generated.gqlDocuments[queryName].export ? "export " : "";
|
|
4412
|
+
gqlDocumentsOutputCode += `${exportStatement} const ${queryName} = gql\`${generated.gqlDocuments[queryName].document}\`\n`;
|
|
4413
|
+
}
|
|
4414
|
+
await writeGenerated(codeOuputFilename, outputCode);
|
|
4415
|
+
writtenFiles.push(codeOuputFilename);
|
|
4416
|
+
if (gqlDocumentsOutputCode != "") {
|
|
4417
|
+
const gqlDocumentsOuputFilename = `${targetDirectory}/${basename(file.replace(/\.cometGen\.tsx?$/, ""))}.gql.tsx`;
|
|
4418
|
+
await promises.rm(gqlDocumentsOuputFilename, { force: true });
|
|
4419
|
+
gqlDocumentsOutputCode = `import { gql } from "@apollo/client";
|
|
4420
|
+
import { finalFormFileUploadFragment, finalFormFileUploadDownloadableFragment } from "@comet/cms-admin";
|
|
4421
|
+
|
|
4422
|
+
${gqlDocumentsOutputCode}
|
|
4423
|
+
`;
|
|
4424
|
+
await writeGenerated(gqlDocumentsOuputFilename, gqlDocumentsOutputCode);
|
|
4425
|
+
writtenFiles.push(gqlDocumentsOuputFilename);
|
|
4426
|
+
}
|
|
4427
|
+
console.log("");
|
|
4428
|
+
}
|
|
4429
|
+
if (writtenFiles.length > 0) {
|
|
4430
|
+
console.log("Formatting generated files...");
|
|
4431
|
+
await exec$1(`./node_modules/.bin/prettier --write ${writtenFiles.join(" ")}`);
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
const generateCommand = new Command("generate").option("-f, --file <file>", "path to config file or glob pattern to generate specific files").action(async ({ file: filePattern }) => {
|
|
4435
|
+
console.log("️️️⚠️️️⚠️️️⚠️️️ Admin Generator is still experimental and in beta phase. ⚠️️️⚠️️️⚠️️️\n\n");
|
|
4436
|
+
await runGenerate(filePattern);
|
|
4437
|
+
});
|
|
4438
|
+
|
|
4439
|
+
//#endregion
|
|
4440
|
+
export { defineConfig, injectFormVariables };
|