@comet/admin-generator 8.7.0-canary-20251112090955 → 8.7.0-canary-20251113072631

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.
@@ -6,7 +6,7 @@ import { type GridCellParams, type GridFilterItem, type GridFilterOperator, type
6
6
  import { Command } from "commander";
7
7
  import { type FieldValidator } from "final-form";
8
8
  import { type ComponentType } from "react";
9
- import { type UsableFields } from "./generateGrid/usableFields";
9
+ import { type UsableFields, type UsableFormFields } from "./generateGrid/usableFields";
10
10
  import { type ColumnVisibleOption } from "./utils/columnVisibility";
11
11
  type IconObject = Pick<IconProps, "color" | "fontSize"> & {
12
12
  name: IconName;
@@ -60,35 +60,35 @@ type AsyncSelectFilter = {
60
60
  };
61
61
  export type FormFieldConfig<T> = (({
62
62
  type: "text";
63
- name: keyof T;
63
+ name: UsableFormFields<T>;
64
64
  multiline?: boolean;
65
65
  } & InputBaseFieldConfig) | ({
66
66
  type: "number";
67
- name: keyof T;
67
+ name: UsableFormFields<T>;
68
68
  decimals?: number;
69
69
  } & InputBaseFieldConfig) | ({
70
70
  type: "numberRange";
71
- name: keyof T;
71
+ name: UsableFormFields<T>;
72
72
  minValue: number;
73
73
  maxValue: number;
74
74
  disableSlider?: boolean;
75
75
  } & InputBaseFieldConfig) | {
76
76
  type: "boolean";
77
- name: keyof T;
77
+ name: UsableFormFields<T>;
78
78
  } | ({
79
79
  type: "date";
80
- name: keyof T;
80
+ name: UsableFormFields<T>;
81
81
  } & InputBaseFieldConfig) | ({
82
82
  type: "dateTime";
83
- name: keyof T;
83
+ name: UsableFormFields<T>;
84
84
  } & InputBaseFieldConfig) | ({
85
85
  type: "staticSelect";
86
- name: keyof T;
86
+ name: UsableFormFields<T>;
87
87
  values?: StaticSelectValue[];
88
88
  inputType?: "select" | "radio";
89
89
  } & Omit<InputBaseFieldConfig, "endAdornment">) | ({
90
90
  type: "asyncSelect";
91
- name: keyof T;
91
+ name: UsableFormFields<T>;
92
92
  rootQuery: string;
93
93
  labelField?: string;
94
94
  /** Whether Autocomplete or Select should be used.
@@ -117,18 +117,18 @@ export type FormFieldConfig<T> = (({
117
117
  filter?: AsyncSelectFilter;
118
118
  } & Omit<InputBaseFieldConfig, "endAdornment">) | {
119
119
  type: "block";
120
- name: keyof T;
120
+ name: UsableFormFields<T>;
121
121
  block: BlockInterface;
122
122
  } | ({
123
123
  type: "fileUpload";
124
124
  multiple?: false;
125
- name: keyof T;
125
+ name: UsableFormFields<T>;
126
126
  maxFiles?: 1;
127
127
  download?: boolean;
128
128
  } & Pick<Partial<FinalFormFileUploadProps<false>>, "maxFileSize" | "readOnly" | "layout" | "accept">) | ({
129
129
  type: "fileUpload";
130
130
  multiple: true;
131
- name: keyof T;
131
+ name: UsableFormFields<T>;
132
132
  maxFiles?: number;
133
133
  download?: boolean;
134
134
  } & Pick<Partial<FinalFormFileUploadProps<true>>, "maxFileSize" | "readOnly" | "layout" | "accept">)) & {
@@ -141,7 +141,7 @@ export type FormFieldConfig<T> = (({
141
141
  export declare function isFormFieldConfig<T>(arg: any): arg is FormFieldConfig<T>;
142
142
  type OptionalNestedFieldsConfig<T> = {
143
143
  type: "optionalNestedFields";
144
- name: keyof T;
144
+ name: UsableFormFields<T>;
145
145
  checkboxLabel?: string;
146
146
  fields: FormFieldConfig<any>[];
147
147
  };
@@ -106,16 +106,16 @@ function generateAsyncSelect({ gqlIntrospection, baseOutputFilename, config, for
106
106
  const formProps = [];
107
107
  const { name, fieldLabel, startAdornment,
108
108
  //endAdornment,
109
- imports: optionsImports, } = (0, options_1.buildFormFieldOptions)({ config, formConfig, gqlIntrospection, gqlType });
109
+ imports: optionsImports, } = (0, options_1.buildFormFieldOptions)({ config, formConfig });
110
110
  imports.push(...optionsImports);
111
111
  const nameWithPrefix = `${namePrefix ? `${namePrefix}.` : ``}${name}`;
112
112
  const required = !(0, isFieldOptional_1.isFieldOptional)({ config, gqlIntrospection, gqlType });
113
113
  const formValueConfig = {
114
- destructFromFormValues: config.type == "asyncSelectFilter" ? name : undefined,
114
+ fieldName: name,
115
+ destructFromFormValues: config.type == "asyncSelectFilter",
115
116
  };
116
117
  let finalFormConfig;
117
118
  let code = "";
118
- let formValueToGqlInputCode = "";
119
119
  const { objectType, multiple } = findIntrospectionObjectType({
120
120
  config,
121
121
  gqlIntrospection,
@@ -278,14 +278,14 @@ function generateAsyncSelect({ gqlIntrospection, baseOutputFilename, config, for
278
278
  if (config.type != "asyncSelectFilter") {
279
279
  if (!multiple) {
280
280
  if (!required) {
281
- formValueToGqlInputCode = `${name}: formValues.${name} ? formValues.${name}.id : null,`;
281
+ formValueConfig.formValueToGqlInputCode = `$fieldName ? $fieldName.id : null`;
282
282
  }
283
283
  else {
284
- formValueToGqlInputCode = `${name}: formValues.${name}?.id,`;
284
+ formValueConfig.formValueToGqlInputCode = `$fieldName?.id`;
285
285
  }
286
286
  }
287
287
  else {
288
- formValueToGqlInputCode = `${name}: formValues.${name}.map((item) => item.id),`;
288
+ formValueConfig.formValueToGqlInputCode = `$fieldName.map((item) => item.id)`;
289
289
  }
290
290
  }
291
291
  imports.push({
@@ -305,8 +305,8 @@ function generateAsyncSelect({ gqlIntrospection, baseOutputFilename, config, for
305
305
  const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
306
306
  if (config.type == "asyncSelectFilter") {
307
307
  // add (in the gql schema) non existing value for virtual filter field
308
- formValueConfig.typeCode = `${name}?: { id: string; ${labelField}: string };`;
309
- formValueConfig.initializationCode = `${name}: data.${instanceGqlType}.${config.loadValueQueryField.replace(/\./g, "?.")}`;
308
+ formValueConfig.typeCode = { nullable: true, type: `{ id: string; ${labelField}: string }` };
309
+ formValueConfig.initializationCode = `data.${instanceGqlType}.${config.loadValueQueryField.replace(/\./g, "?.")}`;
310
310
  }
311
311
  code = `<${useAutocomplete ? "AsyncAutocompleteField" : "AsyncSelectField"}
312
312
  ${required ? "required" : ""}
@@ -354,7 +354,6 @@ function generateAsyncSelect({ gqlIntrospection, baseOutputFilename, config, for
354
354
  return {
355
355
  code,
356
356
  hooksCode: "",
357
- formValueToGqlInputCode,
358
357
  formFragmentFields,
359
358
  gqlDocuments: {},
360
359
  imports,
@@ -0,0 +1,6 @@
1
+ import { type IntrospectionQuery } from "graphql";
2
+ export declare function findIntrospectionFieldType({ name, gqlType, gqlIntrospection, }: {
3
+ name: string;
4
+ gqlType: string;
5
+ gqlIntrospection: IntrospectionQuery;
6
+ }): import("graphql").IntrospectionNamedTypeRef<import("graphql").IntrospectionOutputType> | import("graphql").IntrospectionListTypeRef<import("graphql").IntrospectionOutputTypeRef> | undefined;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findIntrospectionFieldType = findIntrospectionFieldType;
4
+ function findIntrospectionFieldType({ name, gqlType, gqlIntrospection, }) {
5
+ let introspectionFieldType;
6
+ for (const namePart of name.split(".")) {
7
+ const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
8
+ if (!introspectionObject)
9
+ throw new Error(`didn't find object ${gqlType} in gql introspection`);
10
+ const introspectionField = introspectionObject.fields.find((field) => field.name === namePart);
11
+ introspectionFieldType = introspectionField
12
+ ? introspectionField.type.kind === "NON_NULL"
13
+ ? introspectionField.type.ofType
14
+ : introspectionField.type
15
+ : undefined;
16
+ if ((introspectionFieldType === null || introspectionFieldType === void 0 ? void 0 : introspectionFieldType.kind) === "OBJECT") {
17
+ // for next loop iteration (nested fields)
18
+ gqlType = introspectionFieldType.name;
19
+ }
20
+ }
21
+ return introspectionFieldType;
22
+ }
@@ -1,4 +1,3 @@
1
- import { type IntrospectionQuery } from "graphql";
2
1
  import { type FormConfig, type FormFieldConfig } from "../../generate-command";
3
2
  import { type Imports } from "../../utils/generateImportsCode";
4
3
  type AdornmentData = {
@@ -11,11 +10,9 @@ type AdornmentData = {
11
10
  /**
12
11
  * Helper function that builds various options needed for generating form fields.
13
12
  */
14
- export declare function buildFormFieldOptions({ config, formConfig, gqlIntrospection, gqlType, }: {
13
+ export declare function buildFormFieldOptions({ config, formConfig, }: {
15
14
  config: FormFieldConfig<any>;
16
15
  formConfig: FormConfig<any>;
17
- gqlIntrospection: IntrospectionQuery;
18
- gqlType: string;
19
16
  }): {
20
17
  name: string;
21
18
  formattedMessageRootId: any;
@@ -23,6 +20,5 @@ export declare function buildFormFieldOptions({ config, formConfig, gqlIntrospec
23
20
  startAdornment: AdornmentData;
24
21
  endAdornment: AdornmentData;
25
22
  imports: Imports;
26
- introspectionFieldType: import("graphql").IntrospectionNamedTypeRef<import("graphql").IntrospectionOutputType> | import("graphql").IntrospectionListTypeRef<import("graphql").IntrospectionOutputTypeRef> | undefined;
27
23
  };
28
24
  export {};
@@ -51,22 +51,13 @@ const buildAdornmentData = ({ adornmentData }) => {
51
51
  /**
52
52
  * Helper function that builds various options needed for generating form fields.
53
53
  */
54
- function buildFormFieldOptions({ config, formConfig, gqlIntrospection, gqlType, }) {
54
+ function buildFormFieldOptions({ config, formConfig, }) {
55
55
  var _a;
56
56
  const rootGqlType = formConfig.gqlType;
57
57
  const name = String(config.name);
58
58
  const label = (_a = config.label) !== null && _a !== void 0 ? _a : (0, camelCaseToHumanReadable_1.camelCaseToHumanReadable)(name);
59
59
  const formattedMessageRootId = rootGqlType[0].toLowerCase() + rootGqlType.substring(1);
60
60
  const fieldLabel = `<FormattedMessage id="${formattedMessageRootId}.${name}" defaultMessage="${label}" />`;
61
- const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
62
- if (!introspectionObject)
63
- throw new Error(`didn't find object ${gqlType} in gql introspection`);
64
- const introspectionField = introspectionObject.fields.find((field) => field.name === name);
65
- const introspectionFieldType = introspectionField
66
- ? introspectionField.type.kind === "NON_NULL"
67
- ? introspectionField.type.ofType
68
- : introspectionField.type
69
- : undefined;
70
61
  const imports = [];
71
62
  let startAdornment = { adornmentString: "" };
72
63
  let endAdornment = { adornmentString: "" };
@@ -86,5 +77,5 @@ function buildFormFieldOptions({ config, formConfig, gqlIntrospection, gqlType,
86
77
  imports.push(endAdornment.adornmentImport);
87
78
  }
88
79
  }
89
- return { name, formattedMessageRootId, fieldLabel, startAdornment, endAdornment, imports, introspectionFieldType };
80
+ return { name, formattedMessageRootId, fieldLabel, startAdornment, endAdornment, imports };
90
81
  }
@@ -14,7 +14,6 @@ function generateComponentFormField({ config }) {
14
14
  formProps: [],
15
15
  hooksCode: "",
16
16
  formFragmentFields: [],
17
- formValueToGqlInputCode: "",
18
17
  formValuesConfig: [],
19
18
  finalFormConfig: undefined,
20
19
  code,
@@ -6,14 +6,19 @@ export type GenerateFieldsReturn = GeneratorReturn & {
6
6
  imports: Imports;
7
7
  hooksCode: string;
8
8
  formFragmentFields: string[];
9
- formValueToGqlInputCode: string;
10
9
  formProps: Prop[];
11
10
  formValuesConfig: {
12
- omitFromFragmentType?: string;
13
- destructFromFormValues?: string;
14
- typeCode?: string;
11
+ fieldName: string;
12
+ omitFromFragmentType?: boolean;
13
+ destructFromFormValues?: boolean;
14
+ typeCode?: {
15
+ nullable: boolean;
16
+ type: string;
17
+ };
15
18
  initializationCode?: string;
16
19
  defaultInitializationCode?: string;
20
+ formValueToGqlInputCode?: string;
21
+ wrapFormValueToGqlInputCode?: string;
17
22
  }[];
18
23
  finalFormConfig?: {
19
24
  subscription?: {
@@ -22,7 +22,6 @@ function findFieldByName(name, fields) {
22
22
  function generateFields({ gqlIntrospection, baseOutputFilename, fields, formFragmentName, formConfig, gqlType, namePrefix, }) {
23
23
  const gqlDocuments = {};
24
24
  let hooksCode = "";
25
- let formValueToGqlInputCode = "";
26
25
  const formFragmentFields = [];
27
26
  const imports = [];
28
27
  const formProps = [];
@@ -68,7 +67,6 @@ function generateFields({ gqlIntrospection, baseOutputFilename, fields, formFrag
68
67
  imports.push(...generated.imports);
69
68
  formProps.push(...generated.formProps);
70
69
  hooksCode += generated.hooksCode;
71
- formValueToGqlInputCode += generated.formValueToGqlInputCode;
72
70
  formFragmentFields.push(...generated.formFragmentFields);
73
71
  formValuesConfig.push(...generated.formValuesConfig);
74
72
  finalFormConfig.subscription = Object.assign(Object.assign({}, finalFormConfig.subscription), (_a = generated.finalFormConfig) === null || _a === void 0 ? void 0 : _a.subscription);
@@ -79,7 +77,6 @@ function generateFields({ gqlIntrospection, baseOutputFilename, fields, formFrag
79
77
  return {
80
78
  code,
81
79
  hooksCode,
82
- formValueToGqlInputCode,
83
80
  formFragmentFields,
84
81
  gqlDocuments,
85
82
  imports,
@@ -288,7 +288,7 @@ config) {
288
288
 
289
289
  ${customFilterByFragment}
290
290
 
291
- ${(0, generateFormValues_1.generateFormValuesType)({ config, formValuesConfig, filterByFragmentType })}
291
+ ${(0, generateFormValues_1.generateFormValuesType)({ formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType })}
292
292
 
293
293
  ${formPropsTypeCode}
294
294
 
@@ -308,7 +308,7 @@ config) {
308
308
  `
309
309
  : ""}
310
310
 
311
- ${(0, generateFormValues_1.generateInitialValues)({ config, formValuesConfig, filterByFragmentType })}
311
+ ${(0, generateFormValues_1.generateInitialValues)({ mode, formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType })}
312
312
 
313
313
 
314
314
  ${editMode
@@ -326,9 +326,13 @@ config) {
326
326
  `
327
327
  : ""}
328
328
 
329
- const handleSubmit = async (${(0, generateFormValues_1.generateDestructFormValueForInput)({ formValuesConfig })}: FormValues, form: FormApi<FormValues>${addMode ? `, event: FinalFormSubmitEvent` : ""}) => {
329
+ const handleSubmit = async (${(0, generateFormValues_1.generateDestructFormValueForInput)({
330
+ formValuesConfig,
331
+ gqlIntrospection,
332
+ gqlType,
333
+ })}: FormValues, form: FormApi<FormValues>${addMode ? `, event: FinalFormSubmitEvent` : ""}) => {
330
334
  ${editMode ? `if (await saveConflict.checkForConflicts()) throw new Error("Conflicts detected");` : ""}
331
- ${(0, generateFormValues_1.generateFormValuesToGqlInput)({ generatedFields })}
335
+ ${(0, generateFormValues_1.generateFormValuesToGqlInput)({ formValuesConfig, gqlIntrospection, gqlType })}
332
336
 
333
337
  ${mode == "all" ? `if (mode === "edit") {` : ""}
334
338
  ${editMode
@@ -6,6 +6,7 @@ const convertConfigImport_1 = require("../utils/convertConfigImport");
6
6
  const isFieldOptional_1 = require("../utils/isFieldOptional");
7
7
  const runtimeTypeGuards_1 = require("../utils/runtimeTypeGuards");
8
8
  const generateAsyncSelect_1 = require("./asyncSelect/generateAsyncSelect");
9
+ const findIntrospectionFieldType_1 = require("./formField/findIntrospectionFieldType");
9
10
  const options_1 = require("./formField/options");
10
11
  function generateFormField({ gqlIntrospection, baseOutputFilename, config, formConfig, gqlType, namePrefix, }) {
11
12
  if (config.type == "asyncSelect" || config.type == "asyncSelectFilter") {
@@ -13,9 +14,9 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
13
14
  }
14
15
  const imports = [];
15
16
  const formProps = [];
16
- const { name, formattedMessageRootId, fieldLabel, startAdornment, endAdornment, imports: optionsImports, } = (0, options_1.buildFormFieldOptions)({ config, formConfig, gqlType, gqlIntrospection });
17
+ const { name, formattedMessageRootId, fieldLabel, startAdornment, endAdornment, imports: optionsImports, } = (0, options_1.buildFormFieldOptions)({ config, formConfig });
17
18
  imports.push(...optionsImports);
18
- let { introspectionFieldType } = (0, options_1.buildFormFieldOptions)({ config, formConfig, gqlType, gqlIntrospection });
19
+ let introspectionFieldType = (0, findIntrospectionFieldType_1.findIntrospectionFieldType)({ name, gqlIntrospection, gqlType });
19
20
  const nameWithPrefix = `${namePrefix ? `${namePrefix}.` : ``}${name}`;
20
21
  const rootGqlType = formConfig.gqlType;
21
22
  const dataRootName = rootGqlType[0].toLowerCase() + rootGqlType.substring(1); // TODO should probably be deteced via query
@@ -24,7 +25,9 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
24
25
  const endAdornmentWithLockIconProp = `endAdornment={<InputAdornment position="end"><Lock /></InputAdornment>}`;
25
26
  const readOnlyProps = `readOnly disabled`;
26
27
  const readOnlyPropsWithLock = `${readOnlyProps} ${endAdornmentWithLockIconProp}`;
27
- let formValuesConfig = [{}]; // FormFields should only contain one entry
28
+ const formValueConfig = {
29
+ fieldName: nameWithPrefix,
30
+ };
28
31
  const gqlDocuments = {};
29
32
  const hooksCode = "";
30
33
  let finalFormConfig;
@@ -40,7 +43,6 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
40
43
  }
41
44
  }
42
45
  let code = "";
43
- let formValueToGqlInputCode = "";
44
46
  let formFragmentFields = [name];
45
47
  if (config.type == "text") {
46
48
  const TextInputComponent = config.multiline ? "TextAreaField" : "TextField";
@@ -62,7 +64,7 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
62
64
  ${validateCode}
63
65
  />`;
64
66
  if (!required && !config.readOnly) {
65
- formValueToGqlInputCode = `${name}: formValues.${name} ?? null,`;
67
+ formValueConfig.formValueToGqlInputCode = `$fieldName ?? null`;
66
68
  }
67
69
  }
68
70
  else if (config.type == "number") {
@@ -85,22 +87,21 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
85
87
  ${validateCode}
86
88
  />`;
87
89
  //TODO MUI suggest not using type=number https://mui.com/material-ui/react-text-field/#type-quot-number-quot
88
- let assignment = `parseFloat(formValues.${nameWithPrefix})`;
90
+ let assignment = `parseFloat($fieldName)`;
89
91
  if ((0, isFieldOptional_1.isFieldOptional)({ config, gqlIntrospection: gqlIntrospection, gqlType: gqlType })) {
90
- assignment = `formValues.${nameWithPrefix} ? ${assignment} : null`;
92
+ assignment = `$fieldName ? ${assignment} : null`;
91
93
  }
92
- formValueToGqlInputCode = `${name}: ${assignment},`;
94
+ formValueConfig.formValueToGqlInputCode = `${assignment}`;
93
95
  let initializationAssignment = `String(data.${dataRootName}.${nameWithPrefix})`;
94
96
  if (!required) {
95
97
  initializationAssignment = `data.${dataRootName}.${nameWithPrefix} ? ${initializationAssignment} : undefined`;
96
98
  }
97
- formValuesConfig = [
98
- {
99
- omitFromFragmentType: name,
100
- typeCode: `${name}${!required ? `?` : ``}: string;`,
101
- initializationCode: `${name}: ${initializationAssignment}`,
102
- },
103
- ];
99
+ formValueConfig.omitFromFragmentType = true;
100
+ formValueConfig.typeCode = {
101
+ nullable: !required,
102
+ type: "string",
103
+ };
104
+ formValueConfig.initializationCode = `${initializationAssignment}`;
104
105
  }
105
106
  else if (config.type === "numberRange") {
106
107
  code = `
@@ -140,11 +141,7 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
140
141
  : ""}
141
142
  ${validateCode}
142
143
  />`;
143
- formValuesConfig = [
144
- {
145
- defaultInitializationCode: `${name}: false`,
146
- },
147
- ];
144
+ formValueConfig.defaultInitializationCode = `false`;
148
145
  }
149
146
  else if (config.type == "date") {
150
147
  imports.push({
@@ -169,7 +166,7 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
169
166
  ${validateCode}
170
167
  />`;
171
168
  if (!required && !config.readOnly) {
172
- formValueToGqlInputCode = `${name}: formValues.${name} ?? null,`;
169
+ formValueConfig.formValueToGqlInputCode = `$fieldName ?? null`;
173
170
  }
174
171
  }
175
172
  else if (config.type == "dateTime") {
@@ -193,31 +190,25 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
193
190
  : ""}
194
191
  ${validateCode}
195
192
  />`;
196
- formValuesConfig = [
197
- {
198
- initializationCode: `${name}: data.${dataRootName}.${nameWithPrefix} ? new Date(data.${dataRootName}.${nameWithPrefix}) : undefined`,
199
- omitFromFragmentType: name,
200
- typeCode: `${name}${!required ? "?" : ""}: Date${!required ? " | null" : ""};`,
201
- },
202
- ];
193
+ formValueConfig.initializationCode = `data.${dataRootName}.${nameWithPrefix} ? new Date(data.${dataRootName}.${nameWithPrefix}) : undefined`;
194
+ formValueConfig.omitFromFragmentType = true;
195
+ formValueConfig.typeCode = {
196
+ nullable: !required,
197
+ type: `Date${!required ? " | null" : ""}`,
198
+ };
203
199
  if (!config.readOnly) {
204
- formValueToGqlInputCode = required
205
- ? `${name}: formValues.${name}.toISOString(),`
206
- : `${name}: formValues.${name} ? formValues.${name}.toISOString() : null,`;
200
+ formValueConfig.formValueToGqlInputCode = required ? `$fieldName.toISOString()` : `$fieldName ? $fieldName.toISOString() : null`;
207
201
  }
208
202
  }
209
203
  else if (config.type == "block") {
210
204
  code = `<Field name="${nameWithPrefix}" isEqual={isEqual} label={${fieldLabel}} variant="horizontal" fullWidth>
211
205
  {createFinalFormBlock(rootBlocks.${String(config.name)})}
212
206
  </Field>`;
213
- formValueToGqlInputCode = `${name}: rootBlocks.${name}.state2Output(formValues.${nameWithPrefix}),`;
214
- formValuesConfig = [
215
- {
216
- typeCode: `${name}: BlockState<typeof rootBlocks.${name}>;`,
217
- initializationCode: `${name}: rootBlocks.${name}.input2State(data.${dataRootName}.${nameWithPrefix})`,
218
- defaultInitializationCode: `${name}: rootBlocks.${name}.defaultValues()`,
219
- },
220
- ];
207
+ formValueConfig.formValueToGqlInputCode = `rootBlocks.${name}.state2Output($fieldName)`;
208
+ formValueConfig.omitFromFragmentType = true;
209
+ formValueConfig.typeCode = { nullable: false, type: `BlockState<typeof rootBlocks.${name}>` };
210
+ formValueConfig.initializationCode = `rootBlocks.${name}.input2State(data.${dataRootName}.${nameWithPrefix})`;
211
+ formValueConfig.defaultInitializationCode = `rootBlocks.${name}.defaultValues()`;
221
212
  }
222
213
  else if (config.type === "fileUpload") {
223
214
  const multiple = config.multiple || (typeof config.maxFiles === "number" && config.maxFiles > 1);
@@ -231,10 +222,10 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
231
222
  ${config.accept ? `accept="${config.accept}"` : ""}
232
223
  />`;
233
224
  if (multiple) {
234
- formValueToGqlInputCode = `${name}: formValues.${name}?.map(({ id }) => id),`;
225
+ formValueConfig.formValueToGqlInputCode = `$fieldName?.map(({ id }) => id)`;
235
226
  }
236
227
  else {
237
- formValueToGqlInputCode = `${name}: formValues.${name} ? formValues.${name}.id : null,`;
228
+ formValueConfig.formValueToGqlInputCode = `$fieldName ? $fieldName.id : null`;
238
229
  }
239
230
  formFragmentFields = [`${name}...${config.download ? "FinalFormFileUploadDownloadable" : "FinalFormFileUpload"}`];
240
231
  }
@@ -336,12 +327,11 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
336
327
  return {
337
328
  code,
338
329
  hooksCode,
339
- formValueToGqlInputCode,
340
330
  formFragmentFields,
341
331
  gqlDocuments,
342
332
  imports,
343
333
  formProps,
344
- formValuesConfig,
334
+ formValuesConfig: [formValueConfig],
345
335
  finalFormConfig,
346
336
  };
347
337
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateFormLayout = generateFormLayout;
4
4
  const camelCaseToHumanReadable_1 = require("../utils/camelCaseToHumanReadable");
5
+ const findIntrospectionFieldType_1 = require("./formField/findIntrospectionFieldType");
5
6
  const generateFields_1 = require("./generateFields");
6
7
  function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, formFragmentName, formConfig, gqlType, namePrefix, }) {
7
8
  var _a, _b, _c, _d, _e;
@@ -10,7 +11,6 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
10
11
  const dataRootName = rootGqlType[0].toLowerCase() + rootGqlType.substring(1); // TODO should probably be deteced via query
11
12
  let code = "";
12
13
  let hooksCode = "";
13
- let formValueToGqlInputCode = "";
14
14
  const formFragmentFields = [];
15
15
  const gqlDocuments = {};
16
16
  const imports = [];
@@ -29,7 +29,6 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
29
29
  namePrefix,
30
30
  });
31
31
  hooksCode += generatedFields.hooksCode;
32
- formValueToGqlInputCode += generatedFields.formValueToGqlInputCode;
33
32
  formFragmentFields.push(...generatedFields.formFragmentFields);
34
33
  for (const name in generatedFields.gqlDocuments) {
35
34
  gqlDocuments[name] = generatedFields.gqlDocuments[name];
@@ -66,16 +65,10 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
66
65
  }
67
66
  else if (config.type === "optionalNestedFields") {
68
67
  const name = String(config.name);
69
- const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
70
- if (!introspectionObject)
71
- throw new Error(`didn't find object ${gqlType} in gql introspection`);
72
- const introspectionField = introspectionObject.fields.find((field) => field.name === name);
73
- if (!introspectionField)
74
- throw new Error(`didn't find field ${name} in gql introspection type ${gqlType}`);
75
- if (introspectionField.type.kind === "NON_NULL") {
76
- throw new Error(`field ${name} in gql introspection type ${gqlType} must not be required to be usable with optionalNestedFields`);
77
- }
78
- if (introspectionField.type.kind !== "OBJECT")
68
+ const introspectionFieldType = (0, findIntrospectionFieldType_1.findIntrospectionFieldType)({ name, gqlType, gqlIntrospection });
69
+ if (!introspectionFieldType)
70
+ throw new Error(`field ${name} in gql introspection type ${gqlType} not found`);
71
+ if (introspectionFieldType.kind !== "OBJECT")
79
72
  throw new Error(`field ${name} in gql introspection type ${gqlType} has to be OBJECT`);
80
73
  const checkboxLabel = (_e = config.checkboxLabel) !== null && _e !== void 0 ? _e : `Enable ${(0, camelCaseToHumanReadable_1.camelCaseToHumanReadable)(String(config.name))}`;
81
74
  const generatedFields = (0, generateFields_1.generateFields)({
@@ -84,7 +77,7 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
84
77
  fields: config.fields,
85
78
  formFragmentName,
86
79
  formConfig,
87
- gqlType: introspectionField.type.name,
80
+ gqlType: introspectionFieldType.name,
88
81
  namePrefix: name,
89
82
  });
90
83
  hooksCode += generatedFields.hooksCode;
@@ -93,39 +86,26 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
93
86
  gqlDocuments[name] = generatedFields.gqlDocuments[name];
94
87
  }
95
88
  imports.push(...generatedFields.imports);
96
- const wrappingFormValuesConfig = {
97
- omitFromFragmentType: name,
98
- destructFromFormValues: `${name}Enabled`,
99
- typeCode: `${name}Enabled: boolean;`,
100
- initializationCode: `${name}Enabled: !!data.${dataRootName}.${name}`,
101
- };
102
- const subfieldsFormValuesTypeCode = generatedFields.formValuesConfig
103
- .filter((config) => !!config.omitFromFragmentType)
104
- .map((config) => `"${config.omitFromFragmentType}"`);
105
- if (subfieldsFormValuesTypeCode.length) {
106
- wrappingFormValuesConfig.typeCode = `${wrappingFormValuesConfig.typeCode}
107
- ${name}: Omit<NonNullable<GQL${formFragmentName}Fragment["${name}"]>, ${subfieldsFormValuesTypeCode.join(" | ")}> & {
108
- ${generatedFields.formValuesConfig.map((config) => config.typeCode).join("\n")}
109
- };`;
110
- }
111
- const subfieldsFormValuesInitCode = generatedFields.formValuesConfig
112
- .filter((config) => !!config.initializationCode)
113
- .map((config) => config.initializationCode);
114
- if (subfieldsFormValuesInitCode.length) {
115
- wrappingFormValuesConfig.initializationCode = `${wrappingFormValuesConfig.initializationCode},
116
- ${name}: data.${dataRootName}.${name} ? { ${subfieldsFormValuesInitCode.join(", ")}} : undefined `;
117
- }
118
- const subfieldsFormValuesDefaultInitCode = generatedFields.formValuesConfig
119
- .filter((config) => !!config.defaultInitializationCode)
120
- .map((config) => config.defaultInitializationCode);
121
- if (subfieldsFormValuesDefaultInitCode.length) {
122
- wrappingFormValuesConfig.defaultInitializationCode = `${name}: { ${subfieldsFormValuesDefaultInitCode.join(", ")}}`;
123
- }
124
- formValuesConfig.push(wrappingFormValuesConfig);
89
+ formValuesConfig.push(...generatedFields.formValuesConfig);
90
+ // first field is the "enabled" checkbox
91
+ formValuesConfig.push({
92
+ fieldName: `${name}Enabled`,
93
+ omitFromFragmentType: false,
94
+ destructFromFormValues: true,
95
+ typeCode: {
96
+ nullable: false,
97
+ type: "boolean",
98
+ },
99
+ initializationCode: `!!data.${dataRootName}.${name}`,
100
+ });
101
+ // second field is the nested object, which is not a final-form field itself
102
+ formValuesConfig.push({
103
+ fieldName: `${name}`,
104
+ wrapFormValueToGqlInputCode: `${name.split(".").pop()}Enabled && $fieldName ? $inner : null`,
105
+ });
125
106
  imports.push({ name: "FinalFormSwitch", importPath: "@comet/admin" });
126
107
  imports.push({ name: "messages", importPath: "@comet/admin" });
127
108
  imports.push({ name: "FormControlLabel", importPath: "@mui/material" });
128
- formValueToGqlInputCode += `${String(config.name)}: ${String(config.name)}Enabled && formValues.${String(config.name)} ? {${generatedFields.formValueToGqlInputCode}} : null,`;
129
109
  code = `<Field
130
110
  fullWidth
131
111
  name="${String(config.name)}Enabled"
@@ -155,7 +135,6 @@ function generateFormLayout({ gqlIntrospection, baseOutputFilename, config, form
155
135
  return {
156
136
  code,
157
137
  hooksCode,
158
- formValueToGqlInputCode,
159
138
  formFragmentFields,
160
139
  gqlDocuments,
161
140
  imports,
@@ -1,18 +1,39 @@
1
- import { type FormConfig } from "../generate-command";
1
+ import { type IntrospectionQuery } from "graphql";
2
2
  import { type GenerateFieldsReturn } from "./generateFields";
3
- export declare function generateFormValuesType({ config, formValuesConfig, filterByFragmentType, }: {
4
- config: FormConfig<any>;
3
+ type FormValuesConfigTreeNode = {
4
+ config?: GenerateFieldsReturn["formValuesConfig"][0];
5
+ nullable?: boolean;
6
+ children: FormValuesConfigTree;
7
+ };
8
+ type FormValuesConfigTree = {
9
+ [key: string]: FormValuesConfigTreeNode;
10
+ };
11
+ export declare function formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType, }: {
12
+ formValuesConfig: GenerateFieldsReturn["formValuesConfig"];
13
+ gqlIntrospection: IntrospectionQuery;
14
+ gqlType: string;
15
+ }): FormValuesConfigTree;
16
+ export declare function generateFormValuesType({ formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType, }: {
5
17
  formValuesConfig: GenerateFieldsReturn["formValuesConfig"];
6
18
  filterByFragmentType: string;
19
+ gqlIntrospection: IntrospectionQuery;
20
+ gqlType: string;
7
21
  }): string;
8
- export declare function generateInitialValues({ config, formValuesConfig, filterByFragmentType, }: {
9
- config: FormConfig<any>;
22
+ export declare function generateInitialValues({ mode, formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType, }: {
23
+ mode: "all" | "edit" | "add";
10
24
  formValuesConfig: GenerateFieldsReturn["formValuesConfig"];
11
25
  filterByFragmentType: string;
26
+ gqlIntrospection: IntrospectionQuery;
27
+ gqlType: string;
12
28
  }): string;
13
- export declare function generateDestructFormValueForInput({ formValuesConfig }: {
29
+ export declare function generateDestructFormValueForInput({ formValuesConfig, gqlIntrospection, gqlType, }: {
14
30
  formValuesConfig: GenerateFieldsReturn["formValuesConfig"];
31
+ gqlIntrospection: IntrospectionQuery;
32
+ gqlType: string;
15
33
  }): string;
16
- export declare function generateFormValuesToGqlInput({ generatedFields }: {
17
- generatedFields: GenerateFieldsReturn;
34
+ export declare function generateFormValuesToGqlInput({ formValuesConfig, gqlIntrospection, gqlType, }: {
35
+ formValuesConfig: GenerateFieldsReturn["formValuesConfig"];
36
+ gqlIntrospection: IntrospectionQuery;
37
+ gqlType: string;
18
38
  }): string;
39
+ export {};
@@ -1,77 +1,217 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formValuesConfigToTree = formValuesConfigToTree;
3
4
  exports.generateFormValuesType = generateFormValuesType;
4
5
  exports.generateInitialValues = generateInitialValues;
5
6
  exports.generateDestructFormValueForInput = generateDestructFormValueForInput;
6
7
  exports.generateFormValuesToGqlInput = generateFormValuesToGqlInput;
7
- const flatFormFieldsFromFormConfig_1 = require("./flatFormFieldsFromFormConfig");
8
- function generateFormValuesType({ config, formValuesConfig, filterByFragmentType, }) {
9
- const formFields = (0, flatFormFieldsFromFormConfig_1.flatFormFieldsFromFormConfig)(config);
10
- const rootBlockFields = formFields
11
- .filter((field) => field.type == "block")
12
- .map((field) => {
13
- // map is for ts to infer block type correctly
14
- if (field.type !== "block")
15
- throw new Error("Field is not a block field");
16
- return field;
17
- });
18
- return `type FormValues = ${formValuesConfig.filter((config) => !!config.omitFromFragmentType).length > 0 || rootBlockFields.length > 0
19
- ? `Omit<${filterByFragmentType}, ${[
20
- ...(rootBlockFields.length > 0 ? ["keyof typeof rootBlocks"] : []),
21
- ...formValuesConfig.filter((config) => !!config.omitFromFragmentType).map((config) => `"${config.omitFromFragmentType}"`),
22
- ].join(" | ")}>`
23
- : `${filterByFragmentType}`} ${formValuesConfig.filter((config) => !!config.typeCode).length > 0
24
- ? `& {
25
- ${formValuesConfig
26
- .filter((config) => !!config.typeCode)
27
- .map((config) => config.typeCode)
28
- .join("\n")}
29
- }`
30
- : ""};`;
8
+ // internal represenstation of formValuesConfig as tree to allow recursive processing
9
+ function formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType, }) {
10
+ const treeRoot = { children: {} };
11
+ for (const formValueConfig of formValuesConfig) {
12
+ const fieldName = formValueConfig.fieldName;
13
+ let currentTreeNode = treeRoot;
14
+ let currentGqlType = gqlType;
15
+ for (const part of fieldName.split(".")) {
16
+ const introspectionObject = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === currentGqlType);
17
+ if (!introspectionObject)
18
+ throw new Error(`didn't find object ${gqlType} in gql introspection`);
19
+ const introspectionField = introspectionObject.fields.find((field) => field.name === part);
20
+ const introspectionFieldType = introspectionField
21
+ ? introspectionField.type.kind === "NON_NULL"
22
+ ? introspectionField.type.ofType
23
+ : introspectionField.type
24
+ : undefined;
25
+ if ((introspectionFieldType === null || introspectionFieldType === void 0 ? void 0 : introspectionFieldType.kind) === "OBJECT") {
26
+ // for next loop iteration (nested fields)
27
+ currentGqlType = introspectionFieldType.name;
28
+ }
29
+ if (!currentTreeNode.children[part]) {
30
+ currentTreeNode.children[part] = { children: {} };
31
+ }
32
+ currentTreeNode.children[part].nullable = (introspectionField === null || introspectionField === void 0 ? void 0 : introspectionField.type.kind) !== "NON_NULL";
33
+ currentTreeNode = currentTreeNode.children[part];
34
+ }
35
+ currentTreeNode.config = formValueConfig;
36
+ }
37
+ return treeRoot.children;
31
38
  }
32
- function generateInitialValues({ config, formValuesConfig, filterByFragmentType, }) {
39
+ function generateFormValuesTypeFromTree(tree, currentTypeName, currentIsNullable) {
40
+ var _a, _b, _c;
41
+ const omitKeys = [];
42
+ let appendCode = "";
43
+ for (const [key, value] of Object.entries(tree)) {
44
+ if (Object.keys(value.children).length > 0) {
45
+ let childRootType = `${currentTypeName}["${key}"]`;
46
+ if (currentIsNullable) {
47
+ childRootType = `NonNullable<${currentTypeName}>["${key}"]`;
48
+ }
49
+ const childOmit = generateFormValuesTypeFromTree(value.children, childRootType, (_a = value.nullable) !== null && _a !== void 0 ? _a : false);
50
+ if (childOmit !== childRootType) {
51
+ appendCode += `${key}: ${childOmit}`;
52
+ omitKeys.push(key);
53
+ }
54
+ if ((_b = value.config) === null || _b === void 0 ? void 0 : _b.typeCode) {
55
+ throw new Error("Field has both subfields and direct typeCode, which is not supported.");
56
+ }
57
+ if ((_c = value.config) === null || _c === void 0 ? void 0 : _c.omitFromFragmentType) {
58
+ throw new Error("Field has both subfields and direct omitFromFragmentType, which is not supported.");
59
+ }
60
+ }
61
+ else if (value.config) {
62
+ if (value.config.typeCode) {
63
+ appendCode += `${key}${value.config.typeCode.nullable ? "?" : ""}: ${value.config.typeCode.type}; `;
64
+ }
65
+ if (value.config.omitFromFragmentType) {
66
+ omitKeys.push(key);
67
+ }
68
+ }
69
+ }
70
+ let code = omitKeys.length
71
+ ? `Omit<${currentIsNullable ? `NonNullable<` : ""}${currentTypeName}${currentIsNullable ? `>` : ""}, ${omitKeys.map((k) => `"${k}"`).join(" | ")}>`
72
+ : currentTypeName;
73
+ if (appendCode.length) {
74
+ code += ` & { ${appendCode} }`;
75
+ }
76
+ return code;
77
+ }
78
+ function generateFormValuesType({ formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType, }) {
79
+ const tree = formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType });
80
+ return `type FormValues = ${generateFormValuesTypeFromTree(tree, filterByFragmentType, false)};`;
81
+ }
82
+ function generateInitialValuesFromTree(tree, dataObject, generationType) {
33
83
  var _a;
34
- const gqlType = config.gqlType;
84
+ let code = "";
85
+ for (const [key, value] of Object.entries(tree)) {
86
+ if (Object.keys(value.children).length > 0) {
87
+ let childCode = generateInitialValuesFromTree(value.children, `${dataObject}.${key}`, generationType);
88
+ if (childCode.length) {
89
+ if (generationType == "initializationCode") {
90
+ childCode = `{ ...${dataObject}.${key}, ${childCode} }`;
91
+ if (value.nullable) {
92
+ code += `${key}: ${dataObject}.${key} ? ${childCode} : undefined, `;
93
+ }
94
+ else {
95
+ code += `${key}: ${childCode}, `;
96
+ }
97
+ }
98
+ else {
99
+ code += `${key}: { ${childCode} }, `;
100
+ }
101
+ }
102
+ if ((_a = value.config) === null || _a === void 0 ? void 0 : _a[generationType]) {
103
+ throw new Error("Field has both subfields and direct initialization code, which is not supported.");
104
+ }
105
+ }
106
+ else if (value.config) {
107
+ if (value.config[generationType]) {
108
+ code += `${key}: ${value.config[generationType]}, `;
109
+ }
110
+ }
111
+ }
112
+ return code;
113
+ }
114
+ function generateInitialValues({ mode, formValuesConfig, filterByFragmentType, gqlIntrospection, gqlType, }) {
35
115
  const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
36
- const mode = (_a = config.mode) !== null && _a !== void 0 ? _a : "all";
37
116
  const editMode = mode === "edit" || mode == "all";
117
+ const tree = formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType });
38
118
  if (editMode) {
39
119
  return `const initialValues = useMemo<Partial<FormValues>>(() => data?.${instanceGqlType}
40
120
  ? {
41
121
  ...filterByFragment<${filterByFragmentType}>(${instanceGqlType}FormFragment, data.${instanceGqlType}),
42
- ${formValuesConfig
43
- .filter((config) => !!config.initializationCode)
44
- .map((config) => config.initializationCode)
45
- .join(",\n")}
122
+ ${generateInitialValuesFromTree(tree, `data.${instanceGqlType}`, "initializationCode")}
46
123
  }
47
124
  : {
48
- ${formValuesConfig
49
- .filter((config) => !!config.defaultInitializationCode)
50
- .map((config) => config.defaultInitializationCode)
51
- .join(",\n")}
125
+ ${generateInitialValuesFromTree(tree, `data.${instanceGqlType}`, "defaultInitializationCode")}
52
126
  }
53
127
  , [data]);`;
54
128
  }
55
129
  else {
56
130
  return `const initialValues = {
57
- ${formValuesConfig
58
- .filter((config) => !!config.defaultInitializationCode)
59
- .map((config) => config.defaultInitializationCode)
60
- .join(",\n")}
131
+ ${generateInitialValuesFromTree(tree, `data.${instanceGqlType}`, "defaultInitializationCode")}
61
132
  };`;
62
133
  }
63
134
  }
64
- function generateDestructFormValueForInput({ formValuesConfig }) {
65
- return formValuesConfig.filter((config) => !!config.destructFromFormValues).length
66
- ? `{ ${formValuesConfig
67
- .filter((config) => !!config.destructFromFormValues)
68
- .map((config) => config.destructFromFormValues)
69
- .join(", ")}, ...formValues }`
70
- : `formValues`;
135
+ function generateDestructFormValueForInputFromTree(tree, restObject) {
136
+ let code = "";
137
+ for (const [key, value] of Object.entries(tree)) {
138
+ if (Object.keys(value.children).length > 0) {
139
+ const childCode = generateDestructFormValueForInputFromTree(value.children, `${restObject}${key.substring(0, 1).toUpperCase()}${key.substring(1)}`);
140
+ if (childCode.length) {
141
+ code += `${key}: { ${childCode} }, `;
142
+ }
143
+ }
144
+ else if (value.config) {
145
+ if (value.config.destructFromFormValues) {
146
+ code += `${key}, `;
147
+ }
148
+ }
149
+ }
150
+ if (code.length) {
151
+ code += `...${restObject}Rest`;
152
+ return code;
153
+ }
154
+ else {
155
+ return "";
156
+ }
157
+ }
158
+ function generateDestructFormValueForInput({ formValuesConfig, gqlIntrospection, gqlType, }) {
159
+ const tree = formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType });
160
+ const code = generateDestructFormValueForInputFromTree(tree, "formValues");
161
+ return code.length ? `{ ${code} }` : "formValues";
162
+ }
163
+ function generateFormValuesToGqlInputFromTree(tree, dataObject, restObject) {
164
+ var _a, _b;
165
+ let code = "";
166
+ for (const [key, value] of Object.entries(tree)) {
167
+ if (Object.keys(value.children).length > 0) {
168
+ const childRestObject = `${restObject.replace(/Rest$/, "")}${key.substring(0, 1).toUpperCase()}${key.substring(1)}Rest`;
169
+ const hasChildDestruct = hasChildDestructTree(value.children);
170
+ let childCode = generateFormValuesToGqlInputFromTree(value.children, hasChildDestruct ? childRestObject : `${dataObject}.${key}`, childRestObject);
171
+ if (childCode.length || hasChildDestruct) {
172
+ childCode = `{ ...${hasChildDestruct ? childRestObject : `${dataObject}.${key}`}, ${childCode} }`;
173
+ if ((_a = value.config) === null || _a === void 0 ? void 0 : _a.wrapFormValueToGqlInputCode) {
174
+ childCode = value.config.wrapFormValueToGqlInputCode
175
+ .replaceAll("$fieldName", `${dataObject}.${key}`)
176
+ .replaceAll("$inner", childCode);
177
+ }
178
+ code += `${key}: ${childCode}, `;
179
+ }
180
+ if ((_b = value.config) === null || _b === void 0 ? void 0 : _b.formValueToGqlInputCode) {
181
+ throw new Error("Field has both subfields and direct formValueToGqlInputCode, which is not supported.");
182
+ }
183
+ }
184
+ else if (value.config) {
185
+ if (value.config.formValueToGqlInputCode) {
186
+ code += `${key}: ${value.config.formValueToGqlInputCode.replaceAll("$fieldName", `${dataObject}.${key}`)}, `;
187
+ }
188
+ }
189
+ }
190
+ return code;
191
+ }
192
+ function hasChildDestructTree(tree) {
193
+ return Object.values(tree).some((childValue) => {
194
+ var _a;
195
+ if ((_a = childValue.config) === null || _a === void 0 ? void 0 : _a.destructFromFormValues) {
196
+ return true;
197
+ }
198
+ if (Object.keys(childValue.children).length > 0) {
199
+ return hasChildDestructTree(childValue.children);
200
+ }
201
+ return false;
202
+ });
71
203
  }
72
- function generateFormValuesToGqlInput({ generatedFields }) {
73
- return `const output = {
74
- ...formValues,
75
- ${generatedFields.formValueToGqlInputCode}
76
- };`;
204
+ function generateFormValuesToGqlInput({ formValuesConfig, gqlIntrospection, gqlType, }) {
205
+ const tree = formValuesConfigToTree({ formValuesConfig, gqlIntrospection, gqlType });
206
+ const hasChildDestruct = hasChildDestructTree(tree);
207
+ const code = generateFormValuesToGqlInputFromTree(tree, hasChildDestruct ? "formValuesRest" : "formValues", hasChildDestruct ? "formValuesRest" : "formValues");
208
+ if (code.length) {
209
+ return `const output = { ...${hasChildDestruct ? "formValuesRest" : "formValues"}, ${code} };`;
210
+ }
211
+ else if (hasChildDestruct) {
212
+ return `const output = formValuesRest;`;
213
+ }
214
+ else {
215
+ return `const output = formValues;`;
216
+ }
77
217
  }
@@ -3,10 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getForwardedGqlArgs = getForwardedGqlArgs;
4
4
  function getForwardedGqlArgs({ fields, gqlOperation, gqlIntrospection, }) {
5
5
  const ret = [];
6
- const skipGqlInputArgFields = fields.map((field) => String(field.name));
7
6
  getArgsIncludingInputArgSubfields(gqlOperation, gqlIntrospection).forEach((arg) => {
8
- if (arg.isInputArgSubfield && skipGqlInputArgFields.includes(arg.name))
9
- return;
7
+ if (arg.isInputArgSubfield) {
8
+ if (fields.some((field) => {
9
+ return field.name === arg.name || field.name.startsWith(`${arg.name}.`);
10
+ })) {
11
+ // there is a field (or subfield) in this form, no need to forward this arg
12
+ return;
13
+ }
14
+ }
10
15
  let prop;
11
16
  const imports = [];
12
17
  if (arg.type === "ID" || arg.type === "String" || arg.type === "DateTime") {
@@ -2,8 +2,13 @@ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2
2
  type GqlLeaves<T, FollowArrays extends boolean = false, Depth extends number = 5> = [Depth] extends [never] ? never : T extends any ? T extends Array<infer ArrayType> ? FollowArrays extends true ? GqlLeaves<ArrayType, FollowArrays, Prev[Depth]> : never : "__typename" extends keyof T ? {
3
3
  [K in keyof T as K extends "__typename" ? never : K]-?: GqlLeaves<T[K], FollowArrays, Prev[Depth]>;
4
4
  } : never : never;
5
+ type IfExplicitAny<T, Y, N> = T extends never ? Y : N;
5
6
  type FieldNames<T> = {
6
7
  [K in keyof T]: `${Exclude<K, symbol>}${FieldNames<T[K]> extends never ? "" : `.${FieldNames<T[K]>}`}`;
7
8
  }[keyof T];
8
- export type UsableFields<T, FollowArrays extends boolean = false> = FieldNames<GqlLeaves<T, FollowArrays>>;
9
+ export type UsableFields<T, FollowArrays extends boolean = false> = IfExplicitAny<T, any, FieldNames<GqlLeaves<T, FollowArrays>>>;
10
+ type FormFieldNames<T> = {
11
+ [K in keyof T]: `${Exclude<K, symbol>}` | (FormFieldNames<T[K]> extends never ? never : `${Exclude<K, symbol>}.${FormFieldNames<T[K]>}`);
12
+ }[keyof T];
13
+ export type UsableFormFields<T> = IfExplicitAny<T, any, FormFieldNames<GqlLeaves<T, false>>>;
9
14
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comet/admin-generator",
3
- "version": "8.7.0-canary-20251112090955",
3
+ "version": "8.7.0-canary-20251113072631",
4
4
  "description": "Comet Admin Generator CLI tool",
5
5
  "repository": {
6
6
  "directory": "packages/admin/admin-generator",
@@ -47,10 +47,10 @@
47
47
  "rimraf": "^6.0.1",
48
48
  "ts-jest": "^29.4.0",
49
49
  "typescript": "5.8.3",
50
- "@comet/admin": "8.7.0-canary-20251112090955",
51
- "@comet/admin-icons": "8.7.0-canary-20251112090955",
52
- "@comet/cms-admin": "8.7.0-canary-20251112090955",
53
- "@comet/eslint-config": "8.7.0-canary-20251112090955"
50
+ "@comet/admin": "8.7.0-canary-20251113072631",
51
+ "@comet/admin-icons": "8.7.0-canary-20251113072631",
52
+ "@comet/cms-admin": "8.7.0-canary-20251113072631",
53
+ "@comet/eslint-config": "8.7.0-canary-20251113072631"
54
54
  },
55
55
  "engines": {
56
56
  "node": ">=22.0.0"