@comet/admin-generator 8.0.0-beta.3 → 8.0.0-beta.5
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.js +3 -0
- package/lib/commands/generate/config/parseConfig.d.ts +1 -0
- package/lib/commands/generate/config/parseConfig.js +72 -0
- package/lib/commands/generate/config/transformConfig.d.ts +1 -0
- package/lib/commands/generate/config/transformConfig.js +239 -0
- package/lib/commands/generate/generate-command.d.ts +14 -9
- package/lib/commands/generate/generate-command.js +26 -21
- package/lib/commands/generate/generateForm/generateForm.js +51 -36
- package/lib/commands/generate/generateForm/generateFormField.js +14 -4
- package/lib/commands/generate/generateGrid/generateGrid.js +139 -53
- package/lib/commands/generate/generateGrid/generateGridToolbar.js +5 -12
- package/lib/commands/generate/generateGrid/usableFields.d.ts +4 -3
- package/lib/commands/generate/generateGrid/usableFields.js +1 -0
- package/lib/commands/generate/utils/writeGenerated.js +63 -24
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -0
- package/package.json +12 -14
- package/lib/commands/generate/utils/tsMorphHelper.d.ts +0 -4
- package/lib/commands/generate/utils/tsMorphHelper.js +0 -199
package/bin/admin-generator.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseConfig(file: string): Promise<any>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.parseConfig = parseConfig;
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const path_1 = require("path");
|
|
48
|
+
const transformConfig_1 = require("./transformConfig");
|
|
49
|
+
function parseConfig(file) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
//1. parse config file using TypeScript Complier Api and transform it (replace imports and functions that can't be executed)
|
|
52
|
+
const transformedConfig = (0, transformConfig_1.transformConfigFile)(file, yield fs_1.promises.readFile(file, "utf-8"));
|
|
53
|
+
//2. save modified config file to temp file
|
|
54
|
+
const tempFileName = `${(0, path_1.dirname)(file)}/.temp-${(0, path_1.basename)(file)}`;
|
|
55
|
+
yield fs_1.promises.writeFile(tempFileName, transformedConfig, "utf-8");
|
|
56
|
+
//3. import (=execute) temp modified config file
|
|
57
|
+
let executedConfig;
|
|
58
|
+
try {
|
|
59
|
+
const configFile = yield Promise.resolve(`${tempFileName.replace(/\.tsx?$/, "")}`).then(s => __importStar(require(s)));
|
|
60
|
+
if (!configFile.default) {
|
|
61
|
+
throw new Error(`No default export found in ${file}`);
|
|
62
|
+
}
|
|
63
|
+
executedConfig = configFile.default;
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.error(e);
|
|
67
|
+
throw new Error(`Error executing config file ${file}: ${e}`);
|
|
68
|
+
}
|
|
69
|
+
yield fs_1.promises.rm(tempFileName);
|
|
70
|
+
return executedConfig;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function transformConfigFile(fileName: string, sourceText: string): string;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.transformConfigFile = transformConfigFile;
|
|
37
|
+
const ts = __importStar(require("typescript"));
|
|
38
|
+
const supportedImportPaths = [
|
|
39
|
+
"[type=grid].columns.filterOperators",
|
|
40
|
+
"[type=grid].columns.block",
|
|
41
|
+
"[type=grid].columns.component",
|
|
42
|
+
// TODO implement in generator "[type=grid].columns.renderCell",
|
|
43
|
+
//support in up to 5 levels of nested fields (eg. fieldSet)
|
|
44
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`),
|
|
45
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.block`),
|
|
46
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.component`),
|
|
47
|
+
];
|
|
48
|
+
const supportedInlineCodePaths = [
|
|
49
|
+
// TODO implement in generator "[type=grid].columns.filterOperators",
|
|
50
|
+
"[type=grid].columns.renderCell",
|
|
51
|
+
//support in up to 5 levels of nested fields (eg. fieldSet)
|
|
52
|
+
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`),
|
|
53
|
+
];
|
|
54
|
+
// transform the config file to replace all imports and inline code with a { code, imports } object
|
|
55
|
+
// this is needed to be able to execute the config file in a node environment
|
|
56
|
+
function transformConfigFile(fileName, sourceText) {
|
|
57
|
+
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.ES2024, // language version
|
|
58
|
+
true);
|
|
59
|
+
const importedIdentifiers = collectImports(sourceFile);
|
|
60
|
+
function configTransformer() {
|
|
61
|
+
return (context) => {
|
|
62
|
+
const visit = (node, path) => {
|
|
63
|
+
if (ts.isArrowFunction(node)) {
|
|
64
|
+
if (supportedInlineCodePaths.includes(path)) {
|
|
65
|
+
let code = node.getText();
|
|
66
|
+
if (code.endsWith(","))
|
|
67
|
+
code = code.slice(0, -1); // for some unknown reason node can contain the trailing comma
|
|
68
|
+
const imports = findUsedImports(node.body, importedIdentifiers); //find all imports used in the function body
|
|
69
|
+
// replace inline code with { code, imports } object
|
|
70
|
+
return ts.factory.createObjectLiteralExpression([
|
|
71
|
+
ts.factory.createPropertyAssignment("code", ts.factory.createStringLiteral(code)),
|
|
72
|
+
ts.factory.createPropertyAssignment("imports", ts.factory.createArrayLiteralExpression(imports.map((imprt) => {
|
|
73
|
+
return ts.factory.createObjectLiteralExpression([
|
|
74
|
+
ts.factory.createPropertyAssignment("name", ts.factory.createStringLiteral(imprt.name)),
|
|
75
|
+
ts.factory.createPropertyAssignment("import", ts.factory.createStringLiteral(imprt.import)),
|
|
76
|
+
]);
|
|
77
|
+
}))),
|
|
78
|
+
], true);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error(`Inline Function is not allowed here and calling the function is not supported: ${path}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (ts.isIdentifier(node)) {
|
|
85
|
+
const imported = importedIdentifiers.get(node.text);
|
|
86
|
+
if (imported) {
|
|
87
|
+
if (supportedImportPaths.includes(path)) {
|
|
88
|
+
// replace imported identifier with { name, import } object
|
|
89
|
+
return ts.factory.createObjectLiteralExpression([
|
|
90
|
+
ts.factory.createPropertyAssignment("name", ts.factory.createStringLiteral(node.text)),
|
|
91
|
+
ts.factory.createPropertyAssignment("import", ts.factory.createStringLiteral(imported.import)),
|
|
92
|
+
], true);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new Error(`Following the import is not supported: ${path} ${node.text}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const transformKinds = [
|
|
100
|
+
ts.SyntaxKind.Identifier,
|
|
101
|
+
ts.SyntaxKind.ArrayLiteralExpression,
|
|
102
|
+
ts.SyntaxKind.ObjectLiteralExpression,
|
|
103
|
+
ts.SyntaxKind.TaggedTemplateExpression,
|
|
104
|
+
ts.SyntaxKind.SpreadElement,
|
|
105
|
+
ts.SyntaxKind.PropertyAssignment,
|
|
106
|
+
ts.SyntaxKind.ShorthandPropertyAssignment,
|
|
107
|
+
];
|
|
108
|
+
if (!transformKinds.includes(node.kind)) {
|
|
109
|
+
// if the node is not one of the transformKinds, stop transformation at this level return it as is
|
|
110
|
+
return node;
|
|
111
|
+
}
|
|
112
|
+
let newPath = path;
|
|
113
|
+
if (path == "") {
|
|
114
|
+
// first entry of path is the type, then property names (. separated) are added
|
|
115
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
116
|
+
const typeProperty = getTypePropertyFromObjectLiteral(node);
|
|
117
|
+
newPath = typeProperty ? `[type=${typeProperty}]` : "";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
if (ts.isPropertyAssignment(node)) {
|
|
122
|
+
newPath = `${path}.${node.name.getText()}`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return ts.visitEachChild(node, (child) => {
|
|
126
|
+
return visit(child, newPath);
|
|
127
|
+
}, context);
|
|
128
|
+
};
|
|
129
|
+
return (node) => ts.visitNode(node, (child) => visit(child, ""));
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const configNode = findConfigNode(sourceFile);
|
|
133
|
+
const transformedConfigNode = ts.transform(configNode, [configTransformer()]).transformed[0];
|
|
134
|
+
const updatedSource = ts.transform(sourceFile, [
|
|
135
|
+
(context) => {
|
|
136
|
+
const visitor = (node) => {
|
|
137
|
+
if (node === configNode)
|
|
138
|
+
return transformedConfigNode;
|
|
139
|
+
return ts.visitEachChild(node, visitor, context);
|
|
140
|
+
};
|
|
141
|
+
return (node) => ts.visitNode(node, visitor);
|
|
142
|
+
},
|
|
143
|
+
]).transformed[0];
|
|
144
|
+
const printer = ts.createPrinter();
|
|
145
|
+
return printer.printFile(updatedSource);
|
|
146
|
+
}
|
|
147
|
+
// finds the config node in the source file (=default export, might be wrapped in defineConfig or uses satisfies)
|
|
148
|
+
function findConfigNode(sourceFile) {
|
|
149
|
+
let ret;
|
|
150
|
+
sourceFile.forEachChild((node) => {
|
|
151
|
+
if (ts.isExportAssignment(node)) {
|
|
152
|
+
const exportedNode = node.expression;
|
|
153
|
+
if (ts.isCallExpression(exportedNode) && exportedNode.expression.getText() == "defineConfig") {
|
|
154
|
+
//export default defineConfig<Foo>({ ... });
|
|
155
|
+
const args = exportedNode.arguments;
|
|
156
|
+
if (args.length != 1) {
|
|
157
|
+
throw new Error(`Expected exactly one argument for defineConfig`);
|
|
158
|
+
}
|
|
159
|
+
ret = args[0];
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
else if (ts.isSatisfiesExpression(exportedNode)) {
|
|
163
|
+
//export default { ... } satisfies GeneratorConfig<Foo>;
|
|
164
|
+
if (ts.isObjectLiteralExpression(exportedNode.expression)) {
|
|
165
|
+
ret = exportedNode.expression;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (ts.isObjectLiteralExpression(exportedNode)) {
|
|
170
|
+
//export default { ... };
|
|
171
|
+
ret = exportedNode;
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
if (!ret) {
|
|
177
|
+
throw new Error(`No default export found, please export the GeneratorConfig as default, preferrable using defineConfig helper.`);
|
|
178
|
+
}
|
|
179
|
+
return ret;
|
|
180
|
+
}
|
|
181
|
+
// simple helper that extracts the value of the type property from a object literal ({ type: "grid" } returns "grid")
|
|
182
|
+
function getTypePropertyFromObjectLiteral(node) {
|
|
183
|
+
for (const property of node.properties) {
|
|
184
|
+
if (ts.isPropertyAssignment(property)) {
|
|
185
|
+
if (property.name.getText() == "type") {
|
|
186
|
+
const propertyAssignmentInitializer = property.initializer;
|
|
187
|
+
if (ts.isStringLiteral(propertyAssignmentInitializer)) {
|
|
188
|
+
return propertyAssignmentInitializer.text;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
// visits ast and collects all imports statements in the source file
|
|
196
|
+
function collectImports(rootNode) {
|
|
197
|
+
const importedIdentifiers = new Map();
|
|
198
|
+
function visit(node) {
|
|
199
|
+
if (ts.isImportDeclaration(node) &&
|
|
200
|
+
node.importClause &&
|
|
201
|
+
node.importClause.namedBindings &&
|
|
202
|
+
ts.isNamedImports(node.importClause.namedBindings)) {
|
|
203
|
+
const moduleSpecifier = node.moduleSpecifier.text;
|
|
204
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
205
|
+
const localName = element.name.text;
|
|
206
|
+
const originalName = element.propertyName ? element.propertyName.text : localName;
|
|
207
|
+
importedIdentifiers.set(localName, {
|
|
208
|
+
name: originalName,
|
|
209
|
+
import: moduleSpecifier,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
ts.forEachChild(node, visit);
|
|
214
|
+
}
|
|
215
|
+
visit(rootNode);
|
|
216
|
+
return importedIdentifiers;
|
|
217
|
+
}
|
|
218
|
+
// visits ast and collects all identifiers that are an import
|
|
219
|
+
function findUsedImports(rootNode, importedIdentifiers) {
|
|
220
|
+
const imports = [];
|
|
221
|
+
const usedNames = new Set();
|
|
222
|
+
// Collect all identifiers used in the rootNode
|
|
223
|
+
function collectUsedIdentifiers(node) {
|
|
224
|
+
if (ts.isIdentifier(node)) {
|
|
225
|
+
usedNames.add(node.text);
|
|
226
|
+
}
|
|
227
|
+
ts.forEachChild(node, collectUsedIdentifiers);
|
|
228
|
+
}
|
|
229
|
+
collectUsedIdentifiers(rootNode);
|
|
230
|
+
// Match used identifiers to imported ones
|
|
231
|
+
// NOTE: this is not 100% correct as it doesn't recognize cases where a import is overwritten by a local variable. But it is fast.
|
|
232
|
+
for (const name of usedNames) {
|
|
233
|
+
const imported = importedIdentifiers.get(name);
|
|
234
|
+
if (imported) {
|
|
235
|
+
imports.push(imported);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return imports;
|
|
239
|
+
}
|
|
@@ -40,6 +40,7 @@ export type FormFieldConfig<T> = (({
|
|
|
40
40
|
multiline?: boolean;
|
|
41
41
|
} & InputBaseFieldConfig) | ({
|
|
42
42
|
type: "number";
|
|
43
|
+
decimals?: number;
|
|
43
44
|
} & InputBaseFieldConfig) | ({
|
|
44
45
|
type: "numberRange";
|
|
45
46
|
minValue: number;
|
|
@@ -105,13 +106,6 @@ export type FormConfig<T extends {
|
|
|
105
106
|
createMutation?: string;
|
|
106
107
|
fields: (FormFieldConfig<T> | FormLayoutConfig<T> | ComponentFormFieldConfig)[];
|
|
107
108
|
};
|
|
108
|
-
type TabsConfig = {
|
|
109
|
-
type: "tabs";
|
|
110
|
-
tabs: {
|
|
111
|
-
name: string;
|
|
112
|
-
content: GeneratorConfig;
|
|
113
|
-
}[];
|
|
114
|
-
};
|
|
115
109
|
type BaseColumnConfig = Pick<GridColDef, "headerName" | "width" | "minWidth" | "maxWidth" | "flex" | "pinned" | "disableExport"> & {
|
|
116
110
|
headerInfoTooltip?: string;
|
|
117
111
|
visible?: ColumnVisibleOption;
|
|
@@ -127,6 +121,8 @@ export type GridColumnConfig<T extends GridValidRowModel> = ({
|
|
|
127
121
|
renderCell?: (params: GridRenderCellParams<T, any, any>) => JSX.Element;
|
|
128
122
|
} | {
|
|
129
123
|
type: "number";
|
|
124
|
+
currency?: string;
|
|
125
|
+
decimals?: number;
|
|
130
126
|
renderCell?: (params: GridRenderCellParams<T, any, any>) => JSX.Element;
|
|
131
127
|
} | {
|
|
132
128
|
type: "boolean";
|
|
@@ -157,7 +153,7 @@ export type ActionsGridColumnConfig = {
|
|
|
157
153
|
export type VirtualGridColumnConfig<T extends GridValidRowModel> = {
|
|
158
154
|
type: "virtual";
|
|
159
155
|
name: string;
|
|
160
|
-
queryFields?: UsableFields<T>[];
|
|
156
|
+
queryFields?: UsableFields<T, true>[];
|
|
161
157
|
renderCell: (params: GridRenderCellParams<T, any, any>) => JSX.Element;
|
|
162
158
|
} & Pick<GridColDef, "sortBy"> & BaseColumnConfig;
|
|
163
159
|
type InitialFilterConfig = {
|
|
@@ -190,8 +186,17 @@ export type GridConfig<T extends {
|
|
|
190
186
|
newEntryText?: string;
|
|
191
187
|
rowActionProp?: boolean;
|
|
192
188
|
selectionProps?: "multiSelect" | "singleSelect";
|
|
189
|
+
rowReordering?: {
|
|
190
|
+
enabled: boolean;
|
|
191
|
+
dragPreviewField?: UsableFields<T>;
|
|
192
|
+
};
|
|
193
193
|
};
|
|
194
|
-
export type GeneratorConfig
|
|
194
|
+
export type GeneratorConfig<T extends {
|
|
195
|
+
__typename?: string;
|
|
196
|
+
}> = FormConfig<T> | GridConfig<T>;
|
|
197
|
+
export declare function defineConfig<T extends {
|
|
198
|
+
__typename?: string;
|
|
199
|
+
}>(config: GeneratorConfig<T>): GeneratorConfig<T>;
|
|
195
200
|
type GQLDocumentConfig = {
|
|
196
201
|
document: string;
|
|
197
202
|
export: boolean;
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.generateCommand = void 0;
|
|
13
13
|
exports.isFormFieldConfig = isFormFieldConfig;
|
|
14
14
|
exports.isFormLayoutConfig = isFormLayoutConfig;
|
|
15
|
+
exports.defineConfig = defineConfig;
|
|
15
16
|
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
|
|
16
17
|
const load_1 = require("@graphql-tools/load");
|
|
17
18
|
const commander_1 = require("commander");
|
|
@@ -19,9 +20,9 @@ const fs_1 = require("fs");
|
|
|
19
20
|
const glob_1 = require("glob");
|
|
20
21
|
const graphql_1 = require("graphql");
|
|
21
22
|
const path_1 = require("path");
|
|
23
|
+
const parseConfig_1 = require("./config/parseConfig");
|
|
22
24
|
const generateForm_1 = require("./generateForm/generateForm");
|
|
23
25
|
const generateGrid_1 = require("./generateGrid/generateGrid");
|
|
24
|
-
const tsMorphHelper_1 = require("./utils/tsMorphHelper");
|
|
25
26
|
const writeGenerated_1 = require("./utils/writeGenerated");
|
|
26
27
|
function isFormFieldConfig(arg) {
|
|
27
28
|
return !isFormLayoutConfig(arg);
|
|
@@ -29,11 +30,15 @@ function isFormFieldConfig(arg) {
|
|
|
29
30
|
function isFormLayoutConfig(arg) {
|
|
30
31
|
return arg.type !== undefined && ["fieldSet", "optionalNestedFields"].includes(arg.type);
|
|
31
32
|
}
|
|
33
|
+
function defineConfig(config) {
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
32
36
|
/**
|
|
33
37
|
* @experimental
|
|
34
38
|
*/
|
|
35
39
|
function runGenerate() {
|
|
36
40
|
return __awaiter(this, arguments, void 0, function* (filePattern = "src/**/*.cometGen.{ts,tsx}") {
|
|
41
|
+
var _a;
|
|
37
42
|
const schema = yield (0, load_1.loadSchema)("./schema.gql", {
|
|
38
43
|
loaders: [new graphql_file_loader_1.GraphQLFileLoader()],
|
|
39
44
|
});
|
|
@@ -44,28 +49,27 @@ function runGenerate() {
|
|
|
44
49
|
let gqlDocumentsOutputCode = "";
|
|
45
50
|
const targetDirectory = `${(0, path_1.dirname)(file)}/generated`;
|
|
46
51
|
const baseOutputFilename = (0, path_1.basename)(file).replace(/\.cometGen\.tsx?$/, "");
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
console.log(`generating ${file}`);
|
|
53
|
+
const config = yield (0, parseConfig_1.parseConfig)(file);
|
|
49
54
|
const codeOuputFilename = `${targetDirectory}/${(0, path_1.basename)(file.replace(/\.cometGen\.tsx?$/, ""))}.tsx`;
|
|
50
55
|
yield fs_1.promises.rm(codeOuputFilename, { force: true });
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
56
|
+
const exportName = (_a = file.match(/([^/]+)\.cometGen\.tsx?$/)) === null || _a === void 0 ? void 0 : _a[1];
|
|
57
|
+
if (!exportName)
|
|
58
|
+
throw new Error("Can not determine exportName");
|
|
59
|
+
let generated;
|
|
60
|
+
if (config.type == "form") {
|
|
61
|
+
generated = (0, generateForm_1.generateForm)({ exportName, gqlIntrospection, baseOutputFilename, targetDirectory }, config);
|
|
62
|
+
}
|
|
63
|
+
else if (config.type == "grid") {
|
|
64
|
+
generated = (0, generateGrid_1.generateGrid)({ exportName, gqlIntrospection, baseOutputFilename, targetDirectory }, config);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw new Error(`Unknown config type`);
|
|
68
|
+
}
|
|
69
|
+
outputCode += generated.code;
|
|
70
|
+
for (const queryName in generated.gqlDocuments) {
|
|
71
|
+
const exportStatement = generated.gqlDocuments[queryName].export ? "export " : "";
|
|
72
|
+
gqlDocumentsOutputCode += `${exportStatement} const ${queryName} = gql\`${generated.gqlDocuments[queryName].document}\`\n`;
|
|
69
73
|
}
|
|
70
74
|
yield (0, writeGenerated_1.writeGenerated)(codeOuputFilename, outputCode);
|
|
71
75
|
if (gqlDocumentsOutputCode != "") {
|
|
@@ -78,6 +82,7 @@ function runGenerate() {
|
|
|
78
82
|
`;
|
|
79
83
|
yield (0, writeGenerated_1.writeGenerated)(gqlDocumentsOuputFilename, gqlDocumentsOutputCode);
|
|
80
84
|
}
|
|
85
|
+
console.log("");
|
|
81
86
|
}
|
|
82
87
|
});
|
|
83
88
|
}
|
|
@@ -48,7 +48,43 @@ config) {
|
|
|
48
48
|
const instanceGqlType = gqlType[0].toLowerCase() + gqlType.substring(1);
|
|
49
49
|
const formFragmentName = (_a = config.fragmentName) !== null && _a !== void 0 ? _a : `${gqlType}Form`;
|
|
50
50
|
const gqlDocuments = {};
|
|
51
|
-
const imports = [
|
|
51
|
+
const imports = [
|
|
52
|
+
{ name: "FormattedMessage", importPath: "react-intl" },
|
|
53
|
+
{ name: "useApolloClient", importPath: "@apollo/client" },
|
|
54
|
+
{ name: "useQuery", importPath: "@apollo/client" },
|
|
55
|
+
{ name: "gql", importPath: "@apollo/client" },
|
|
56
|
+
{ name: "AsyncSelectField", importPath: "@comet/admin" },
|
|
57
|
+
{ name: "CheckboxField", importPath: "@comet/admin" },
|
|
58
|
+
{ name: "Field", importPath: "@comet/admin" },
|
|
59
|
+
{ name: "filterByFragment", importPath: "@comet/admin" },
|
|
60
|
+
{ name: "FinalForm", importPath: "@comet/admin" },
|
|
61
|
+
{ name: "FinalFormInput", importPath: "@comet/admin" },
|
|
62
|
+
{ name: "FinalFormRangeInput", importPath: "@comet/admin" },
|
|
63
|
+
{ name: "FinalFormSelect", importPath: "@comet/admin" },
|
|
64
|
+
{ name: "FinalFormSubmitEvent", importPath: "@comet/admin" },
|
|
65
|
+
{ name: "Loading", importPath: "@comet/admin" },
|
|
66
|
+
{ name: "NumberField", importPath: "@comet/admin" },
|
|
67
|
+
{ name: "RadioGroupField", importPath: "@comet/admin" },
|
|
68
|
+
{ name: "TextAreaField", importPath: "@comet/admin" },
|
|
69
|
+
{ name: "TextField", importPath: "@comet/admin" },
|
|
70
|
+
{ name: "useFormApiRef", importPath: "@comet/admin" },
|
|
71
|
+
{ name: "useStackSwitchApi", importPath: "@comet/admin" },
|
|
72
|
+
{ name: "ArrowLeft", importPath: "@comet/admin-icons" },
|
|
73
|
+
{ name: "Lock", importPath: "@comet/admin-icons" },
|
|
74
|
+
{ name: "DateTimeField", importPath: "@comet/admin-date-time" },
|
|
75
|
+
{ name: "FinalFormDatePicker", importPath: "@comet/admin-date-time" },
|
|
76
|
+
{ name: "BlockState", importPath: "@comet/cms-admin" },
|
|
77
|
+
{ name: "createFinalFormBlock", importPath: "@comet/cms-admin" },
|
|
78
|
+
{ name: "queryUpdatedAt", importPath: "@comet/cms-admin" },
|
|
79
|
+
{ name: "resolveHasSaveConflict", importPath: "@comet/cms-admin" },
|
|
80
|
+
{ name: "useFormSaveConflict", importPath: "@comet/cms-admin" },
|
|
81
|
+
{ name: "FileUploadField", importPath: "@comet/cms-admin" },
|
|
82
|
+
{ name: "IconButton", importPath: "@mui/material" },
|
|
83
|
+
{ name: "MenuItem", importPath: "@mui/material" },
|
|
84
|
+
{ name: "InputAdornment", importPath: "@mui/material" },
|
|
85
|
+
{ name: "FormApi", importPath: "final-form" },
|
|
86
|
+
{ name: "useMemo", importPath: "react" },
|
|
87
|
+
];
|
|
52
88
|
const props = [];
|
|
53
89
|
const mode = (_b = config.mode) !== null && _b !== void 0 ? _b : "all";
|
|
54
90
|
const editMode = mode === "edit" || mode == "all";
|
|
@@ -219,10 +255,12 @@ config) {
|
|
|
219
255
|
name: `GQL${documentName}${type[0].toUpperCase() + type.substring(1)}`,
|
|
220
256
|
importPath: `./${baseOutputFilename}.gql.generated`,
|
|
221
257
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
258
|
+
if (type !== "fragment") {
|
|
259
|
+
imports.push({
|
|
260
|
+
name: `GQL${documentName}${type[0].toUpperCase() + type.substring(1)}Variables`,
|
|
261
|
+
importPath: `./${baseOutputFilename}.gql.generated`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
226
264
|
}
|
|
227
265
|
const finalFormSubscription = Object.keys((_e = (_d = generatedFields.finalFormConfig) === null || _d === void 0 ? void 0 : _d.subscription) !== null && _e !== void 0 ? _e : {});
|
|
228
266
|
const finalFormRenderProps = Object.keys((_g = (_f = generatedFields.finalFormConfig) === null || _f === void 0 ? void 0 : _f.renderProps) !== null && _g !== void 0 ? _g : {});
|
|
@@ -248,33 +286,10 @@ config) {
|
|
|
248
286
|
}`;
|
|
249
287
|
filterByFragmentType = `${formFragmentName}Fragment`;
|
|
250
288
|
}
|
|
251
|
-
const code = `
|
|
252
|
-
import {
|
|
253
|
-
AsyncSelectField,
|
|
254
|
-
CheckboxField,
|
|
255
|
-
Field,
|
|
256
|
-
filterByFragment,
|
|
257
|
-
FinalForm,
|
|
258
|
-
FinalFormInput,
|
|
259
|
-
FinalFormRangeInput,
|
|
260
|
-
FinalFormSelect,
|
|
261
|
-
FinalFormSubmitEvent,
|
|
262
|
-
Loading,
|
|
263
|
-
RadioGroupField,
|
|
264
|
-
TextAreaField,
|
|
265
|
-
TextField,
|
|
266
|
-
useFormApiRef,
|
|
267
|
-
useStackSwitchApi,
|
|
268
|
-
} from "@comet/admin";
|
|
269
|
-
import { ArrowLeft, Lock } from "@comet/admin-icons";
|
|
270
|
-
import { DateTimeField, FinalFormDatePicker } from "@comet/admin-date-time";
|
|
271
|
-
import { BlockState, createFinalFormBlock, queryUpdatedAt, resolveHasSaveConflict, useFormSaveConflict, FileUploadField } from "@comet/cms-admin";
|
|
272
|
-
import { FormControlLabel, IconButton, MenuItem, InputAdornment } from "@mui/material";
|
|
273
|
-
import { FormApi } from "final-form";
|
|
274
|
-
import isEqual from "lodash.isequal";
|
|
275
|
-
import { useMemo } from "react";
|
|
276
|
-
import { FormattedMessage } from "react-intl";
|
|
289
|
+
const code = `
|
|
277
290
|
${(0, generateImportsCode_1.generateImportsCode)(imports)}
|
|
291
|
+
import isEqual from "lodash.isequal";
|
|
292
|
+
|
|
278
293
|
${rootBlockFields.length > 0
|
|
279
294
|
? `const rootBlocks = {
|
|
280
295
|
${rootBlockFields.map((field) => `${String(field.name)}: ${field.block.name}`)}
|
|
@@ -283,11 +298,11 @@ config) {
|
|
|
283
298
|
|
|
284
299
|
${customFilterByFragment}
|
|
285
300
|
|
|
286
|
-
type FormValues = ${formValuesConfig.filter((config) => !!config.omitFromFragmentType).length > 0
|
|
287
|
-
? `Omit<${filterByFragmentType}, ${
|
|
288
|
-
.
|
|
289
|
-
.map((config) => `"${config.omitFromFragmentType}"`)
|
|
290
|
-
|
|
301
|
+
type FormValues = ${formValuesConfig.filter((config) => !!config.omitFromFragmentType).length > 0 || rootBlockFields.length > 0
|
|
302
|
+
? `Omit<${filterByFragmentType}, ${[
|
|
303
|
+
...(rootBlockFields.length > 0 ? ["keyof typeof rootBlocks"] : []),
|
|
304
|
+
...formValuesConfig.filter((config) => !!config.omitFromFragmentType).map((config) => `"${config.omitFromFragmentType}"`),
|
|
305
|
+
].join(" | ")}>`
|
|
291
306
|
: `${filterByFragmentType}`} ${formValuesConfig.filter((config) => !!config.typeCode).length > 0
|
|
292
307
|
? `& {
|
|
293
308
|
${formValuesConfig
|
|
@@ -140,6 +140,7 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
|
|
|
140
140
|
let formValueToGqlInputCode = "";
|
|
141
141
|
let formFragmentField = name;
|
|
142
142
|
if (config.type == "text") {
|
|
143
|
+
const required = config.required !== undefined ? config.required : false; // don't use inferred from gql here, non-nullable textinput allows empty string
|
|
143
144
|
const TextInputComponent = config.multiline ? "TextAreaField" : "TextField";
|
|
144
145
|
code = `
|
|
145
146
|
<${TextInputComponent}
|
|
@@ -158,18 +159,20 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
|
|
|
158
159
|
: ""}
|
|
159
160
|
${validateCode}
|
|
160
161
|
/>`;
|
|
162
|
+
if (!config.virtual && !required && !config.readOnly) {
|
|
163
|
+
formValueToGqlInputCode = `${name}: formValues.${name} ?? "",`;
|
|
164
|
+
}
|
|
161
165
|
}
|
|
162
166
|
else if (config.type == "number") {
|
|
163
167
|
code = `
|
|
164
|
-
<
|
|
168
|
+
<NumberField
|
|
165
169
|
${required ? "required" : ""}
|
|
166
170
|
${config.readOnly ? readOnlyPropsWithLock : ""}
|
|
167
171
|
variant="horizontal"
|
|
168
172
|
fullWidth
|
|
169
173
|
name="${nameWithPrefix}"
|
|
170
|
-
component={FinalFormInput}
|
|
171
|
-
type="number"
|
|
172
174
|
label={${fieldLabel}}
|
|
175
|
+
${config.decimals ? `decimals={${config.decimals}}` : ""}
|
|
173
176
|
${config.startAdornment ? `startAdornment={<InputAdornment position="start">${startAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
174
177
|
${config.endAdornment ? `endAdornment={<InputAdornment position="end">${endAdornment.adornmentString}</InputAdornment>}` : ""}
|
|
175
178
|
${config.helperText
|
|
@@ -462,7 +465,14 @@ function generateFormField({ gqlIntrospection, baseOutputFilename, config, formC
|
|
|
462
465
|
imports.push({ name: "OnChangeField", importPath: "@comet/admin" });
|
|
463
466
|
finalFormConfig = { subscription: { values: true }, renderProps: { values: true, form: true } };
|
|
464
467
|
}
|
|
465
|
-
|
|
468
|
+
if (!config.virtual) {
|
|
469
|
+
if (!required) {
|
|
470
|
+
formValueToGqlInputCode = `${name}: formValues.${name} ? formValues.${name}.id : null,`;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
formValueToGqlInputCode = `${name}: formValues.${name}?.id,`;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
466
476
|
imports.push({
|
|
467
477
|
name: `GQL${queryName}Query`,
|
|
468
478
|
importPath: `./${baseOutputFilename}.generated`,
|