@comet/admin-generator 8.0.0-beta.4 → 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 +7 -10
- package/lib/commands/generate/generate-command.js +6 -6
- package/lib/commands/generate/generateForm/generateForm.js +51 -37
- package/lib/commands/generate/generateForm/generateFormField.js +1 -0
- package/lib/commands/generate/generateGrid/generateGrid.js +139 -51
- package/lib/commands/generate/generateGrid/generateGridToolbar.js +1 -1
- package/lib/commands/generate/utils/writeGenerated.js +63 -24
- package/package.json +11 -13
- package/lib/commands/generate/utils/tsMorphHelper.d.ts +0 -4
- package/lib/commands/generate/utils/tsMorphHelper.js +0 -216
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
|
+
}
|
|
@@ -106,15 +106,6 @@ export type FormConfig<T extends {
|
|
|
106
106
|
createMutation?: string;
|
|
107
107
|
fields: (FormFieldConfig<T> | FormLayoutConfig<T> | ComponentFormFieldConfig)[];
|
|
108
108
|
};
|
|
109
|
-
type TabsConfig<T extends {
|
|
110
|
-
__typename?: string;
|
|
111
|
-
}> = {
|
|
112
|
-
type: "tabs";
|
|
113
|
-
tabs: {
|
|
114
|
-
name: string;
|
|
115
|
-
content: GeneratorConfig<T>;
|
|
116
|
-
}[];
|
|
117
|
-
};
|
|
118
109
|
type BaseColumnConfig = Pick<GridColDef, "headerName" | "width" | "minWidth" | "maxWidth" | "flex" | "pinned" | "disableExport"> & {
|
|
119
110
|
headerInfoTooltip?: string;
|
|
120
111
|
visible?: ColumnVisibleOption;
|
|
@@ -130,6 +121,8 @@ export type GridColumnConfig<T extends GridValidRowModel> = ({
|
|
|
130
121
|
renderCell?: (params: GridRenderCellParams<T, any, any>) => JSX.Element;
|
|
131
122
|
} | {
|
|
132
123
|
type: "number";
|
|
124
|
+
currency?: string;
|
|
125
|
+
decimals?: number;
|
|
133
126
|
renderCell?: (params: GridRenderCellParams<T, any, any>) => JSX.Element;
|
|
134
127
|
} | {
|
|
135
128
|
type: "boolean";
|
|
@@ -193,10 +186,14 @@ export type GridConfig<T extends {
|
|
|
193
186
|
newEntryText?: string;
|
|
194
187
|
rowActionProp?: boolean;
|
|
195
188
|
selectionProps?: "multiSelect" | "singleSelect";
|
|
189
|
+
rowReordering?: {
|
|
190
|
+
enabled: boolean;
|
|
191
|
+
dragPreviewField?: UsableFields<T>;
|
|
192
|
+
};
|
|
196
193
|
};
|
|
197
194
|
export type GeneratorConfig<T extends {
|
|
198
195
|
__typename?: string;
|
|
199
|
-
}> = FormConfig<T> | GridConfig<T
|
|
196
|
+
}> = FormConfig<T> | GridConfig<T>;
|
|
200
197
|
export declare function defineConfig<T extends {
|
|
201
198
|
__typename?: string;
|
|
202
199
|
}>(config: GeneratorConfig<T>): GeneratorConfig<T>;
|
|
@@ -20,9 +20,9 @@ const fs_1 = require("fs");
|
|
|
20
20
|
const glob_1 = require("glob");
|
|
21
21
|
const graphql_1 = require("graphql");
|
|
22
22
|
const path_1 = require("path");
|
|
23
|
+
const parseConfig_1 = require("./config/parseConfig");
|
|
23
24
|
const generateForm_1 = require("./generateForm/generateForm");
|
|
24
25
|
const generateGrid_1 = require("./generateGrid/generateGrid");
|
|
25
|
-
const tsMorphHelper_1 = require("./utils/tsMorphHelper");
|
|
26
26
|
const writeGenerated_1 = require("./utils/writeGenerated");
|
|
27
27
|
function isFormFieldConfig(arg) {
|
|
28
28
|
return !isFormLayoutConfig(arg);
|
|
@@ -50,13 +50,12 @@ function runGenerate() {
|
|
|
50
50
|
const targetDirectory = `${(0, path_1.dirname)(file)}/generated`;
|
|
51
51
|
const baseOutputFilename = (0, path_1.basename)(file).replace(/\.cometGen\.tsx?$/, "");
|
|
52
52
|
console.log(`generating ${file}`);
|
|
53
|
-
const config =
|
|
53
|
+
const config = yield (0, parseConfig_1.parseConfig)(file);
|
|
54
|
+
const codeOuputFilename = `${targetDirectory}/${(0, path_1.basename)(file.replace(/\.cometGen\.tsx?$/, ""))}.tsx`;
|
|
55
|
+
yield fs_1.promises.rm(codeOuputFilename, { force: true });
|
|
54
56
|
const exportName = (_a = file.match(/([^/]+)\.cometGen\.tsx?$/)) === null || _a === void 0 ? void 0 : _a[1];
|
|
55
57
|
if (!exportName)
|
|
56
58
|
throw new Error("Can not determine exportName");
|
|
57
|
-
//const configs = await import(`${process.cwd()}/${file.replace(/\.ts$/, "")}`);
|
|
58
|
-
const codeOuputFilename = `${targetDirectory}/${(0, path_1.basename)(file.replace(/\.cometGen\.tsx?$/, ""))}.tsx`;
|
|
59
|
-
yield fs_1.promises.rm(codeOuputFilename, { force: true });
|
|
60
59
|
let generated;
|
|
61
60
|
if (config.type == "form") {
|
|
62
61
|
generated = (0, generateForm_1.generateForm)({ exportName, gqlIntrospection, baseOutputFilename, targetDirectory }, config);
|
|
@@ -65,7 +64,7 @@ function runGenerate() {
|
|
|
65
64
|
generated = (0, generateGrid_1.generateGrid)({ exportName, gqlIntrospection, baseOutputFilename, targetDirectory }, config);
|
|
66
65
|
}
|
|
67
66
|
else {
|
|
68
|
-
throw new Error(`Unknown config type
|
|
67
|
+
throw new Error(`Unknown config type`);
|
|
69
68
|
}
|
|
70
69
|
outputCode += generated.code;
|
|
71
70
|
for (const queryName in generated.gqlDocuments) {
|
|
@@ -83,6 +82,7 @@ function runGenerate() {
|
|
|
83
82
|
`;
|
|
84
83
|
yield (0, writeGenerated_1.writeGenerated)(gqlDocumentsOuputFilename, gqlDocumentsOutputCode);
|
|
85
84
|
}
|
|
85
|
+
console.log("");
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
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,34 +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
|
-
NumberField,
|
|
264
|
-
RadioGroupField,
|
|
265
|
-
TextAreaField,
|
|
266
|
-
TextField,
|
|
267
|
-
useFormApiRef,
|
|
268
|
-
useStackSwitchApi,
|
|
269
|
-
} from "@comet/admin";
|
|
270
|
-
import { ArrowLeft, Lock } from "@comet/admin-icons";
|
|
271
|
-
import { DateTimeField, FinalFormDatePicker } from "@comet/admin-date-time";
|
|
272
|
-
import { BlockState, createFinalFormBlock, queryUpdatedAt, resolveHasSaveConflict, useFormSaveConflict, FileUploadField } from "@comet/cms-admin";
|
|
273
|
-
import { FormControlLabel, IconButton, MenuItem, InputAdornment } from "@mui/material";
|
|
274
|
-
import { FormApi } from "final-form";
|
|
275
|
-
import isEqual from "lodash.isequal";
|
|
276
|
-
import { useMemo } from "react";
|
|
277
|
-
import { FormattedMessage } from "react-intl";
|
|
289
|
+
const code = `
|
|
278
290
|
${(0, generateImportsCode_1.generateImportsCode)(imports)}
|
|
291
|
+
import isEqual from "lodash.isequal";
|
|
292
|
+
|
|
279
293
|
${rootBlockFields.length > 0
|
|
280
294
|
? `const rootBlocks = {
|
|
281
295
|
${rootBlockFields.map((field) => `${String(field.name)}: ${field.block.name}`)}
|
|
@@ -284,11 +298,11 @@ config) {
|
|
|
284
298
|
|
|
285
299
|
${customFilterByFragment}
|
|
286
300
|
|
|
287
|
-
type FormValues = ${formValuesConfig.filter((config) => !!config.omitFromFragmentType).length > 0
|
|
288
|
-
? `Omit<${filterByFragmentType}, ${
|
|
289
|
-
.
|
|
290
|
-
.map((config) => `"${config.omitFromFragmentType}"`)
|
|
291
|
-
|
|
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(" | ")}>`
|
|
292
306
|
: `${filterByFragmentType}`} ${formValuesConfig.filter((config) => !!config.typeCode).length > 0
|
|
293
307
|
? `& {
|
|
294
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}
|
|
@@ -115,7 +115,7 @@ const getValueOptionsLabelData = (messageId, label) => {
|
|
|
115
115
|
};
|
|
116
116
|
};
|
|
117
117
|
function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntrospection, }, config) {
|
|
118
|
-
var _a, _b;
|
|
118
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
119
119
|
const gqlType = config.gqlType;
|
|
120
120
|
const gqlTypePlural = (0, pluralize_1.plural)(gqlType);
|
|
121
121
|
//const title = config.title ?? camelCaseToHumanReadable(gqlType);
|
|
@@ -123,8 +123,58 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
123
123
|
const instanceGqlTypePlural = gqlTypePlural[0].toLowerCase() + gqlTypePlural.substring(1);
|
|
124
124
|
const gridQuery = config.query ? config.query : instanceGqlType != instanceGqlTypePlural ? instanceGqlTypePlural : `${instanceGqlTypePlural}List`;
|
|
125
125
|
const gqlDocuments = {};
|
|
126
|
-
const imports = [
|
|
127
|
-
|
|
126
|
+
const imports = [
|
|
127
|
+
{ name: "FormattedMessage", importPath: "react-intl" },
|
|
128
|
+
{ name: "FormattedNumber", importPath: "react-intl" },
|
|
129
|
+
{ name: "useIntl", importPath: "react-intl" },
|
|
130
|
+
{ name: "ReactNode", importPath: "react" },
|
|
131
|
+
{ name: "gql", importPath: "@apollo/client" },
|
|
132
|
+
{ name: "useApolloClient", importPath: "@apollo/client" },
|
|
133
|
+
{ name: "useQuery", importPath: "@apollo/client" },
|
|
134
|
+
{ name: "Button", importPath: "@comet/admin" },
|
|
135
|
+
{ name: "CrudContextMenu", importPath: "@comet/admin" },
|
|
136
|
+
{ name: "CrudMoreActionsMenu", importPath: "@comet/admin" },
|
|
137
|
+
{ name: "DataGridToolbar", importPath: "@comet/admin" },
|
|
138
|
+
{ name: "ExportApi", importPath: "@comet/admin" },
|
|
139
|
+
{ name: "filterByFragment", importPath: "@comet/admin" },
|
|
140
|
+
{ name: "GridFilterButton", importPath: "@comet/admin" },
|
|
141
|
+
{ name: "GridCellContent", importPath: "@comet/admin" },
|
|
142
|
+
{ name: "GridColDef", importPath: "@comet/admin" },
|
|
143
|
+
{ name: "dataGridDateTimeColumn", importPath: "@comet/admin" },
|
|
144
|
+
{ name: "dataGridDateColumn", importPath: "@comet/admin" },
|
|
145
|
+
{ name: "renderStaticSelectCell", importPath: "@comet/admin" },
|
|
146
|
+
{ name: "messages", importPath: "@comet/admin" },
|
|
147
|
+
{ name: "muiGridFilterToGql", importPath: "@comet/admin" },
|
|
148
|
+
{ name: "muiGridSortToGql", importPath: "@comet/admin" },
|
|
149
|
+
{ name: "StackLink", importPath: "@comet/admin" },
|
|
150
|
+
{ name: "FillSpace", importPath: "@comet/admin" },
|
|
151
|
+
{ name: "Tooltip", importPath: "@comet/admin" },
|
|
152
|
+
{ name: "useBufferedRowCount", importPath: "@comet/admin" },
|
|
153
|
+
{ name: "useDataGridExcelExport", importPath: "@comet/admin" },
|
|
154
|
+
{ name: "useDataGridRemote", importPath: "@comet/admin" },
|
|
155
|
+
{ name: "usePersistentColumnState", importPath: "@comet/admin" },
|
|
156
|
+
{ name: "BlockPreviewContent", importPath: "@comet/cms-admin" },
|
|
157
|
+
{ name: "useContentScope", importPath: "@comet/cms-admin" },
|
|
158
|
+
{ name: "Alert", importPath: "@mui/material" },
|
|
159
|
+
{ name: "Box", importPath: "@mui/material" },
|
|
160
|
+
{ name: "IconButton", importPath: "@mui/material" },
|
|
161
|
+
{ name: "Typography", importPath: "@mui/material" },
|
|
162
|
+
{ name: "useTheme", importPath: "@mui/material" },
|
|
163
|
+
{ name: "Menu", importPath: "@mui/material" },
|
|
164
|
+
{ name: "MenuItem", importPath: "@mui/material" },
|
|
165
|
+
{ name: "ListItemIcon", importPath: "@mui/material" },
|
|
166
|
+
{ name: "ListItemText", importPath: "@mui/material" },
|
|
167
|
+
{ name: "CircularProgress", importPath: "@mui/material" },
|
|
168
|
+
{ name: "DataGridPro", importPath: "@mui/x-data-grid-pro" },
|
|
169
|
+
{ name: "GridLogicOperator", importPath: "@mui/x-data-grid-pro" },
|
|
170
|
+
{ name: "GridRenderCellParams", importPath: "@mui/x-data-grid-pro" },
|
|
171
|
+
{ name: "GridSlotsComponent", importPath: "@mui/x-data-grid-pro" },
|
|
172
|
+
{ name: "GridToolbarProps", importPath: "@mui/x-data-grid-pro" },
|
|
173
|
+
{ name: "GridColumnHeaderTitle", importPath: "@mui/x-data-grid-pro" },
|
|
174
|
+
{ name: "GridToolbarQuickFilter", importPath: "@mui/x-data-grid-pro" },
|
|
175
|
+
{ name: "GridRowOrderChangeParams", importPath: "@mui/x-data-grid-pro" },
|
|
176
|
+
];
|
|
177
|
+
const iconsToImport = ["Add", "Edit", "Info", "Excel"];
|
|
128
178
|
const props = [];
|
|
129
179
|
const fieldList = (0, generateGqlFieldList_1.generateGqlFieldList)({
|
|
130
180
|
columns: config.columns.filter((column) => {
|
|
@@ -161,12 +211,29 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
161
211
|
});
|
|
162
212
|
const gridQueryType = (0, findQueryType_1.findQueryTypeOrThrow)(gridQuery, gqlIntrospection);
|
|
163
213
|
const createMutationType = (0, findMutationType_1.findMutationType)(`create${gqlType}`, gqlIntrospection);
|
|
214
|
+
const updateMutationType = (0, findMutationType_1.findMutationType)(`update${gqlType}`, gqlIntrospection);
|
|
164
215
|
const hasDeleteMutation = !!(0, findMutationType_1.findMutationType)(`delete${gqlType}`, gqlIntrospection);
|
|
165
216
|
const hasCreateMutation = !!createMutationType;
|
|
217
|
+
const hasUpdateMutation = !!updateMutationType;
|
|
166
218
|
const allowCopyPaste = (typeof config.copyPaste === "undefined" || config.copyPaste === true) && !config.readOnly && hasCreateMutation;
|
|
167
219
|
const allowAdding = (typeof config.add === "undefined" || config.add === true) && !config.readOnly;
|
|
168
220
|
const allowEditing = (typeof config.edit === "undefined" || config.edit === true) && !config.readOnly;
|
|
169
221
|
const allowDeleting = (typeof config.delete === "undefined" || config.delete === true) && !config.readOnly && hasDeleteMutation;
|
|
222
|
+
const allowRowReordering = typeof ((_a = config.rowReordering) === null || _a === void 0 ? void 0 : _a.enabled) !== "undefined" && ((_b = config.rowReordering) === null || _b === void 0 ? void 0 : _b.enabled) && hasUpdateMutation;
|
|
223
|
+
const updateInputArg = updateMutationType === null || updateMutationType === void 0 ? void 0 : updateMutationType.args.find((arg) => arg.name === "input");
|
|
224
|
+
if (allowRowReordering && updateInputArg) {
|
|
225
|
+
const inputType = (0, findInputObjectType_1.findInputObjectType)(updateInputArg, gqlIntrospection);
|
|
226
|
+
if (!inputType)
|
|
227
|
+
throw new Error("Can't find update input type");
|
|
228
|
+
if (!((_c = inputType.inputFields) === null || _c === void 0 ? void 0 : _c.find((field) => field.name === "position"))) {
|
|
229
|
+
throw new Error("Position field is needed when using 'rowReordering'");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const hasRowReorderingOnDragField = allowRowReordering && typeof ((_d = config.rowReordering) === null || _d === void 0 ? void 0 : _d.dragPreviewField) !== "undefined";
|
|
233
|
+
if (hasRowReorderingOnDragField &&
|
|
234
|
+
!config.columns.find((column) => { var _a; return column.type !== "actions" && (column === null || column === void 0 ? void 0 : column.name) === ((_a = config.rowReordering) === null || _a === void 0 ? void 0 : _a.dragPreviewField); })) {
|
|
235
|
+
throw new Error(`rowReorderingOnDragField '${(_e = config.rowReordering) === null || _e === void 0 ? void 0 : _e.dragPreviewField}' must exist in columns`);
|
|
236
|
+
}
|
|
170
237
|
const forwardRowAction = allowEditing && config.rowActionProp;
|
|
171
238
|
const showActionsColumn = allowCopyPaste || allowEditing || allowDeleting;
|
|
172
239
|
const showCrudContextMenuInActionsColumn = allowCopyPaste || allowDeleting;
|
|
@@ -175,9 +242,9 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
175
242
|
const { imports: forwardedGqlArgsImports, props: forwardedGqlArgsProps, gqlArgs, } = (0, getForwardedGqlArgs_1.getForwardedGqlArgs)([gridQueryType, ...(createMutationType ? [createMutationType] : [])]);
|
|
176
243
|
imports.push(...forwardedGqlArgsImports);
|
|
177
244
|
props.push(...forwardedGqlArgsProps);
|
|
178
|
-
const renderToolbar = (
|
|
245
|
+
const renderToolbar = (_f = config.toolbar) !== null && _f !== void 0 ? _f : true;
|
|
179
246
|
const filterArg = gridQueryType.args.find((arg) => arg.name === "filter");
|
|
180
|
-
const hasFilter = !!filterArg && renderToolbar;
|
|
247
|
+
const hasFilter = !!filterArg && renderToolbar && !allowRowReordering;
|
|
181
248
|
let hasFilterProp = false;
|
|
182
249
|
let filterFields = [];
|
|
183
250
|
if (filterArg) {
|
|
@@ -223,8 +290,11 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
223
290
|
if (!sortInputEnum)
|
|
224
291
|
throw new Error("Can't find sortInputEnum");
|
|
225
292
|
sortFields = sortInputEnum.enumValues.map((v) => v.name.replace(/_/g, "."));
|
|
293
|
+
if (allowRowReordering && !sortFields.includes("position")) {
|
|
294
|
+
throw new Error("Sort argument must include 'position' field for row reordering");
|
|
295
|
+
}
|
|
226
296
|
}
|
|
227
|
-
const hasSearch = gridQueryType.args.some((arg) => arg.name === "search");
|
|
297
|
+
const hasSearch = gridQueryType.args.some((arg) => arg.name === "search") && !allowRowReordering;
|
|
228
298
|
const hasScope = gridQueryType.args.some((arg) => arg.name === "scope");
|
|
229
299
|
const schemaEntity = gqlIntrospection.__schema.types.find((type) => type.kind === "OBJECT" && type.name === gqlType);
|
|
230
300
|
if (!schemaEntity)
|
|
@@ -247,7 +317,7 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
247
317
|
return true;
|
|
248
318
|
});
|
|
249
319
|
const actionsColumnConfig = config.columns.find((column) => column.type === "actions");
|
|
250
|
-
const
|
|
320
|
+
const _j = actionsColumnConfig !== null && actionsColumnConfig !== void 0 ? actionsColumnConfig : {}, { component: actionsColumnComponent, type: actionsColumnType, headerName: actionsColumnHeaderName, pinned: actionsColumnPinned = "right", width: actionsColumnWidth = defaultActionsColumnWidth, visible: actionsColumnVisible = undefined } = _j, restActionsColumnConfig = __rest(_j, ["component", "type", "headerName", "pinned", "width", "visible"]);
|
|
251
321
|
if (actionsColumnComponent) {
|
|
252
322
|
if (!(0, runtimeTypeGuards_1.isGeneratorConfigImport)(actionsColumnComponent)) {
|
|
253
323
|
throw new Error("Unsupported actionsColumnComponent, only imports are supported");
|
|
@@ -281,6 +351,12 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
281
351
|
}
|
|
282
352
|
else if (type == "number") {
|
|
283
353
|
gridType = "number";
|
|
354
|
+
const defaultDecimals = column.currency ? 2 : 0;
|
|
355
|
+
const decimals = typeof column.decimals === "number" ? column.decimals : defaultDecimals;
|
|
356
|
+
const currencyProps = column.currency ? `style="currency" currency="${column.currency}"` : "";
|
|
357
|
+
renderCell = `({ value }) => {
|
|
358
|
+
return (typeof value === "number") ? <FormattedNumber value={value} ${currencyProps} minimumFractionDigits={${decimals}} maximumFractionDigits={${decimals}} /> : "";
|
|
359
|
+
}`;
|
|
284
360
|
}
|
|
285
361
|
else if (type == "boolean") {
|
|
286
362
|
gridType = "boolean";
|
|
@@ -419,7 +495,7 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
419
495
|
createMutationInputFields = inputType.inputFields.filter((field) => fieldsToLoad.some((gridColumnField) => gridColumnField.name == field.name));
|
|
420
496
|
}
|
|
421
497
|
}
|
|
422
|
-
const fragmentName = (
|
|
498
|
+
const fragmentName = (_g = config.fragmentName) !== null && _g !== void 0 ? _g : `${gqlTypePlural}Form`;
|
|
423
499
|
if (forwardRowAction) {
|
|
424
500
|
props.push({
|
|
425
501
|
name: "rowAction",
|
|
@@ -458,7 +534,7 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
458
534
|
: ""}
|
|
459
535
|
${config.initialFilter
|
|
460
536
|
? `initialFilter:{ ${config.initialFilter.linkOperator
|
|
461
|
-
? `linkOperator:
|
|
537
|
+
? `linkOperator: GridLogicOperator.${config.initialFilter.linkOperator === "or" ? "Or" : "And"},`
|
|
462
538
|
: ""}
|
|
463
539
|
items: [${config.initialFilter.items
|
|
464
540
|
.map((item) => {
|
|
@@ -469,47 +545,17 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
469
545
|
${config.queryParamsPrefix ? `queryParamsPrefix: "${config.queryParamsPrefix}",` : ""}
|
|
470
546
|
}`
|
|
471
547
|
: "";
|
|
472
|
-
const code = `import {
|
|
473
|
-
import {
|
|
474
|
-
Button,
|
|
475
|
-
CrudContextMenu,
|
|
476
|
-
CrudMoreActionsMenu,
|
|
477
|
-
DataGridToolbar,
|
|
478
|
-
ExportApi,
|
|
479
|
-
filterByFragment,
|
|
480
|
-
GridFilterButton,
|
|
481
|
-
GridCellContent,
|
|
482
|
-
GridColDef,
|
|
483
|
-
dataGridDateTimeColumn,
|
|
484
|
-
dataGridDateColumn,
|
|
485
|
-
renderStaticSelectCell,
|
|
486
|
-
messages,
|
|
487
|
-
muiGridFilterToGql,
|
|
488
|
-
muiGridSortToGql,
|
|
489
|
-
StackLink,
|
|
490
|
-
FillSpace,
|
|
491
|
-
Tooltip,
|
|
492
|
-
useBufferedRowCount,
|
|
493
|
-
useDataGridExcelExport,
|
|
494
|
-
useDataGridRemote,
|
|
495
|
-
usePersistentColumnState,
|
|
496
|
-
} from "@comet/admin";
|
|
497
|
-
import { Add as AddIcon, Edit, Info, MoreVertical, Excel } from "@comet/admin-icons";
|
|
498
|
-
import { BlockPreviewContent } from "@comet/cms-admin";
|
|
499
|
-
import { Alert, Box, IconButton, Typography, useTheme, Menu, MenuItem, ListItemIcon, ListItemText, CircularProgress } from "@mui/material";
|
|
500
|
-
import { DataGridPro, GridLinkOperator, GridRenderCellParams, GridSlotsComponent, GridToolbarProps, GridColumnHeaderTitle, GridToolbarQuickFilter } from "@mui/x-data-grid-pro";
|
|
501
|
-
import { useContentScope } from "@src/common/ContentScopeProvider";
|
|
502
|
-
import {
|
|
548
|
+
const code = `import {
|
|
503
549
|
GQL${gqlTypePlural}GridQuery,
|
|
504
550
|
GQL${gqlTypePlural}GridQueryVariables,
|
|
505
551
|
GQL${fragmentName}Fragment,
|
|
506
552
|
GQLCreate${gqlType}Mutation,
|
|
507
553
|
GQLCreate${gqlType}MutationVariables,
|
|
554
|
+
GQLUpdate${gqlType}PositionMutation,
|
|
555
|
+
GQLUpdate${gqlType}PositionMutationVariables,
|
|
508
556
|
GQLDelete${gqlType}Mutation,
|
|
509
557
|
GQLDelete${gqlType}MutationVariables
|
|
510
558
|
} from "./${baseOutputFilename}.generated";
|
|
511
|
-
import { ReactNode } from "react";
|
|
512
|
-
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
|
|
513
559
|
${(0, generateImportsCode_1.generateImportsCode)(imports)}
|
|
514
560
|
|
|
515
561
|
const ${instanceGqlTypePlural}Fragment = gql\`
|
|
@@ -545,6 +591,17 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
545
591
|
\${${instanceGqlTypePlural}Fragment}
|
|
546
592
|
\`;
|
|
547
593
|
|
|
594
|
+
${allowRowReordering
|
|
595
|
+
? `const update${gqlType}PositionMutation = gql\`
|
|
596
|
+
mutation Update${gqlType}Position($id: ID!, $input: ${gqlType}UpdateInput!) {
|
|
597
|
+
update${gqlType}(id: $id, input: $input) {
|
|
598
|
+
id
|
|
599
|
+
position
|
|
600
|
+
updatedAt
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
\`;`
|
|
604
|
+
: ""}
|
|
548
605
|
|
|
549
606
|
${allowDeleting
|
|
550
607
|
? `const delete${gqlType}Mutation = gql\`
|
|
@@ -600,6 +657,8 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
600
657
|
${hasScope ? `const { scope } = useContentScope();` : ""}
|
|
601
658
|
${gridNeedsTheme ? `const theme = useTheme();` : ""}
|
|
602
659
|
|
|
660
|
+
${generateHandleRowOrderChange(allowRowReordering, gqlType, instanceGqlTypePlural)}
|
|
661
|
+
|
|
603
662
|
const columns: GridColDef<GQL${fragmentName}Fragment>[] = [
|
|
604
663
|
${gridColumnFields
|
|
605
664
|
.map((column) => {
|
|
@@ -630,7 +689,7 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
630
689
|
<Tooltip
|
|
631
690
|
title={<FormattedMessage id="${instanceGqlType}.${column.name}.tooltip" defaultMessage="${column.headerInfoTooltip}" />}
|
|
632
691
|
>
|
|
633
|
-
<
|
|
692
|
+
<InfoIcon sx={{ marginLeft: 1 }} />
|
|
634
693
|
</Tooltip>
|
|
635
694
|
</>
|
|
636
695
|
)`
|
|
@@ -639,8 +698,8 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
639
698
|
? `intl.formatMessage({ id: "${instanceGqlType}.${column.name}", defaultMessage: "${column.headerName || (0, camelCaseToHumanReadable_1.camelCaseToHumanReadable)(column.name)}" })`
|
|
640
699
|
: undefined,
|
|
641
700
|
type: column.gridType ? `"${column.gridType}"` : undefined,
|
|
642
|
-
filterable: !column.filterOperators && !filterFields.includes(column.name) ? `false` : undefined,
|
|
643
|
-
sortable: !sortFields.includes(column.name) ? `false` : undefined,
|
|
701
|
+
filterable: (!column.filterOperators && !filterFields.includes(column.name)) || allowRowReordering ? `false` : undefined,
|
|
702
|
+
sortable: !sortFields.includes(column.name) || allowRowReordering ? `false` : undefined,
|
|
644
703
|
valueGetter: column.valueGetter,
|
|
645
704
|
valueFormatter: column.valueFormatter,
|
|
646
705
|
valueOptions: column.valueOptions,
|
|
@@ -753,17 +812,20 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
753
812
|
: []
|
|
754
813
|
: []),
|
|
755
814
|
...(hasSearch ? ["search: gqlSearch"] : []),
|
|
756
|
-
...
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
815
|
+
...(!allowRowReordering
|
|
816
|
+
? [
|
|
817
|
+
`offset: dataGridProps.paginationModel.page * dataGridProps.paginationModel.pageSize`,
|
|
818
|
+
`limit: dataGridProps.paginationModel.pageSize`,
|
|
819
|
+
`sort: muiGridSortToGql(dataGridProps.sortModel)`,
|
|
820
|
+
]
|
|
821
|
+
: // TODO: offset and limit should not be necessary for row reordering but not yet possible to disable in the api generator
|
|
822
|
+
[`offset: 0`, `limit: 100`, `sort: { field: "position", direction: "ASC" }`]),
|
|
761
823
|
].join(", ")}
|
|
762
824
|
},
|
|
763
825
|
});
|
|
764
826
|
const rowCount = useBufferedRowCount(data?.${gridQuery}.totalCount);
|
|
765
827
|
if (error) throw error;
|
|
766
|
-
const rows = data?.${gridQuery}.nodes ?? [];
|
|
828
|
+
const rows = ${!allowRowReordering ? `data?.${gridQuery}.nodes` : generateRowReorderingRows(gridQuery, fieldList, (_h = config.rowReordering) === null || _h === void 0 ? void 0 : _h.dragPreviewField)} ?? [];
|
|
767
829
|
|
|
768
830
|
${generateGridExportApi(config.excelExport, gqlTypePlural, gridQuery)}
|
|
769
831
|
|
|
@@ -779,6 +841,11 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
779
841
|
toolbar: ${gridToolbarComponentName} as GridSlotsComponent["toolbar"],
|
|
780
842
|
}}
|
|
781
843
|
${getDataGridSlotProps(gridToolbarComponentName, forwardToolbarAction, config.excelExport)}`
|
|
844
|
+
: ""}
|
|
845
|
+
${allowRowReordering
|
|
846
|
+
? `rowReordering
|
|
847
|
+
onRowOrderChange={handleRowOrderChange}
|
|
848
|
+
hideFooterPagination`
|
|
782
849
|
: ""}
|
|
783
850
|
/>
|
|
784
851
|
);
|
|
@@ -789,6 +856,14 @@ function generateGrid({ exportName, baseOutputFilename, targetDirectory, gqlIntr
|
|
|
789
856
|
gqlDocuments,
|
|
790
857
|
};
|
|
791
858
|
}
|
|
859
|
+
const generateRowReorderingRows = (gridQuery, fieldList, dragPreviewField) => {
|
|
860
|
+
const fields = fieldList.split(" ");
|
|
861
|
+
const reorderField = dragPreviewField || fields.find((field) => field === "title" || field === "name") || "id";
|
|
862
|
+
return `data?.${gridQuery}.nodes.map((node) => ({
|
|
863
|
+
...node,
|
|
864
|
+
__reorder__: node.${reorderField}
|
|
865
|
+
}))`;
|
|
866
|
+
};
|
|
792
867
|
const getDefaultActionsColumnWidth = (showCrudContextMenu, showEdit) => {
|
|
793
868
|
let numberOfActions = 0;
|
|
794
869
|
if (showCrudContextMenu) {
|
|
@@ -830,3 +905,16 @@ const generateGridExportApi = (excelExport, gqlTypePlural, gridQuery) => {
|
|
|
830
905
|
},
|
|
831
906
|
});`;
|
|
832
907
|
};
|
|
908
|
+
const generateHandleRowOrderChange = (allowRowReordering, gqlType, instanceGqlTypePlural) => {
|
|
909
|
+
if (!allowRowReordering) {
|
|
910
|
+
return "";
|
|
911
|
+
}
|
|
912
|
+
return `const handleRowOrderChange = async ({ row: { id }, targetIndex }: GridRowOrderChangeParams) => {
|
|
913
|
+
await client.mutate<GQLUpdate${gqlType}PositionMutation, GQLUpdate${gqlType}PositionMutationVariables>({
|
|
914
|
+
mutation: update${gqlType}PositionMutation,
|
|
915
|
+
variables: { id, input: { position: targetIndex + 1 } },
|
|
916
|
+
awaitRefetchQueries: true,
|
|
917
|
+
refetchQueries: [${instanceGqlTypePlural}Query]
|
|
918
|
+
});
|
|
919
|
+
};`;
|
|
920
|
+
};
|
|
@@ -59,7 +59,7 @@ const renderToolbarActions = ({ forwardToolbarAction, addItemText, excelExport,
|
|
|
59
59
|
${excelExport
|
|
60
60
|
? `{
|
|
61
61
|
label: <FormattedMessage {...messages.downloadAsExcel} />,
|
|
62
|
-
icon: exportApi.loading ? <CircularProgress size={20} /> : <
|
|
62
|
+
icon: exportApi.loading ? <CircularProgress size={20} /> : <ExcelIcon />,
|
|
63
63
|
onClick: () => exportApi.exportGrid(),
|
|
64
64
|
disabled: exportApi.loading,
|
|
65
65
|
}`
|
|
@@ -46,39 +46,78 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.writeGenerated = writeGenerated;
|
|
49
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
50
|
-
const eslint_1 = require("eslint");
|
|
51
49
|
const fs_1 = require("fs");
|
|
52
50
|
const path = __importStar(require("path"));
|
|
51
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
53
52
|
function writeGenerated(filePath, contents) {
|
|
54
53
|
return __awaiter(this, void 0, void 0, function* () {
|
|
55
54
|
const header = `// This file has been generated by comet admin-generator.
|
|
56
55
|
// You may choose to use this file as scaffold by moving this file out of generated folder and removing this comment.
|
|
57
56
|
`;
|
|
58
57
|
yield fs_1.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
const sourceFile = typescript_1.default.createSourceFile(filePath, header + contents, typescript_1.default.ScriptTarget.Latest, true, typescript_1.default.ScriptKind.TSX);
|
|
59
|
+
const result = typescript_1.default.transform(sourceFile, [removeUnusedImports()]);
|
|
60
|
+
const printer = typescript_1.default.createPrinter({
|
|
61
|
+
newLine: typescript_1.default.NewLineKind.LineFeed,
|
|
62
|
+
removeComments: false,
|
|
62
63
|
});
|
|
63
|
-
|
|
64
|
-
yield fs_1.promises.writeFile(filePath,
|
|
65
|
-
const lintResult = yield eslint.lintText(header + contents, {
|
|
66
|
-
filePath,
|
|
67
|
-
});
|
|
68
|
-
if (lintResult[0].errorCount > 0 || lintResult[0].fatalErrorCount > 0) {
|
|
69
|
-
const errorMessage = lintResult[0].messages
|
|
70
|
-
.map((message) => {
|
|
71
|
-
return message.message;
|
|
72
|
-
})
|
|
73
|
-
.join(".");
|
|
74
|
-
console.error(chalk_1.default.red(`Linting error in ${filePath}: \n${errorMessage}`));
|
|
75
|
-
}
|
|
76
|
-
else if (lintResult[0].output != null) {
|
|
77
|
-
yield fs_1.promises.writeFile(filePath, lintResult[0].output);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
console.error(chalk_1.default.red("No output from linting"));
|
|
81
|
-
}
|
|
64
|
+
const prettyCode = printer.printFile(result.transformed[0]).replace(/gql `/g, "gql`");
|
|
65
|
+
yield fs_1.promises.writeFile(filePath, prettyCode);
|
|
82
66
|
console.log(`generated ${filePath}`);
|
|
83
67
|
});
|
|
84
68
|
}
|
|
69
|
+
// copy from @comet/api-generator
|
|
70
|
+
function removeUnusedImports() {
|
|
71
|
+
return (context) => {
|
|
72
|
+
function visit(sourceFile) {
|
|
73
|
+
const usedIdentifiers = new Set();
|
|
74
|
+
// Step 1: Collect all used identifiers
|
|
75
|
+
function collectUsage(node) {
|
|
76
|
+
if (typescript_1.default.isIdentifier(node)) {
|
|
77
|
+
usedIdentifiers.add(node.text);
|
|
78
|
+
}
|
|
79
|
+
else if (typescript_1.default.isImportDeclaration(node)) {
|
|
80
|
+
// Skip import declarations (those identifiers are not usages)
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
typescript_1.default.forEachChild(node, collectUsage);
|
|
84
|
+
}
|
|
85
|
+
typescript_1.default.forEachChild(sourceFile, collectUsage);
|
|
86
|
+
// Step 2: Visit and update the import declarations
|
|
87
|
+
function visitor(node) {
|
|
88
|
+
if (typescript_1.default.isImportDeclaration(node) && node.importClause) {
|
|
89
|
+
const { name, namedBindings } = node.importClause;
|
|
90
|
+
const updatedBindings = [];
|
|
91
|
+
if (name && usedIdentifiers.has(name.text)) {
|
|
92
|
+
// default import is used
|
|
93
|
+
}
|
|
94
|
+
else if (name) {
|
|
95
|
+
// default import is unused
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (namedBindings && typescript_1.default.isNamedImports(namedBindings)) {
|
|
99
|
+
for (const specifier of namedBindings.elements) {
|
|
100
|
+
const importName = specifier.name.text;
|
|
101
|
+
if (usedIdentifiers.has(importName)) {
|
|
102
|
+
updatedBindings.push(specifier);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (updatedBindings.length === 0 && !name) {
|
|
106
|
+
// nothing left in this import
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return typescript_1.default.factory.updateImportDeclaration(node, node.modifiers, typescript_1.default.factory.updateImportClause(node.importClause, node.importClause.isTypeOnly, name, updatedBindings.length > 0 ? typescript_1.default.factory.updateNamedImports(namedBindings, updatedBindings) : undefined), node.moduleSpecifier, node.assertClause);
|
|
110
|
+
}
|
|
111
|
+
if (!namedBindings && !name) {
|
|
112
|
+
// empty import, probably a side-effect import
|
|
113
|
+
return node;
|
|
114
|
+
}
|
|
115
|
+
return node;
|
|
116
|
+
}
|
|
117
|
+
return typescript_1.default.visitEachChild(node, visitor, context);
|
|
118
|
+
}
|
|
119
|
+
return typescript_1.default.visitNode(sourceFile, visitor);
|
|
120
|
+
}
|
|
121
|
+
return (sourceFile) => visit(sourceFile);
|
|
122
|
+
};
|
|
123
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/admin-generator",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.5",
|
|
4
4
|
"repository": {
|
|
5
5
|
"directory": "packages/admin/admin-generator",
|
|
6
6
|
"type": "git",
|
|
@@ -19,24 +19,22 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@graphql-tools/graphql-file-loader": "^7.5.17",
|
|
21
21
|
"@graphql-tools/load": "^7.8.14",
|
|
22
|
-
"chalk": "^5.4.1",
|
|
23
22
|
"change-case": "^4.1.2",
|
|
24
23
|
"commander": "^9.5.0",
|
|
25
24
|
"glob": "^10.4.5",
|
|
26
25
|
"graphql": "^16.10.0",
|
|
27
26
|
"object-path": "^0.11.8",
|
|
28
27
|
"pluralize": "^8.0.0",
|
|
29
|
-
"ts-morph": "^25.0.1",
|
|
30
28
|
"ts-node": "^10.9.2"
|
|
31
29
|
},
|
|
32
30
|
"devDependencies": {
|
|
33
|
-
"@mui/material": "^7.
|
|
34
|
-
"@mui/x-data-grid": "^7.
|
|
31
|
+
"@mui/material": "^7.1.0",
|
|
32
|
+
"@mui/x-data-grid": "^7.29.4",
|
|
35
33
|
"@types/jest": "^29.5.14",
|
|
36
|
-
"@types/node": "^22.
|
|
34
|
+
"@types/node": "^22.15.21",
|
|
37
35
|
"@types/object-path": "^0.11.4",
|
|
38
36
|
"@types/pluralize": "^0.0.33",
|
|
39
|
-
"@types/react": "^18.3.
|
|
37
|
+
"@types/react": "^18.3.22",
|
|
40
38
|
"eslint": "^9.22.0",
|
|
41
39
|
"final-form": "^4.20.10",
|
|
42
40
|
"jest": "^29.7.0",
|
|
@@ -47,10 +45,10 @@
|
|
|
47
45
|
"rimraf": "^6.0.1",
|
|
48
46
|
"ts-jest": "^29.2.6",
|
|
49
47
|
"typescript": "^5.7.3",
|
|
50
|
-
"@comet/admin": "8.0.0-beta.
|
|
51
|
-
"@comet/admin-icons": "8.0.0-beta.
|
|
52
|
-
"@comet/cms-admin": "8.0.0-beta.
|
|
53
|
-
"@comet/eslint-config": "8.0.0-beta.
|
|
48
|
+
"@comet/admin": "8.0.0-beta.5",
|
|
49
|
+
"@comet/admin-icons": "8.0.0-beta.5",
|
|
50
|
+
"@comet/cms-admin": "8.0.0-beta.5",
|
|
51
|
+
"@comet/eslint-config": "8.0.0-beta.5"
|
|
54
52
|
},
|
|
55
53
|
"engines": {
|
|
56
54
|
"node": ">=22.0.0"
|
|
@@ -67,7 +65,7 @@
|
|
|
67
65
|
"lint:eslint": "eslint --max-warnings 0 src/ **/*.json --no-warn-ignored",
|
|
68
66
|
"lint:prettier": "npx prettier --check './**/*.{js,json,md,yml,yaml}'",
|
|
69
67
|
"lint:tsc": "tsc",
|
|
70
|
-
"test": "jest --passWithNoTests",
|
|
71
|
-
"test:watch": "jest --watch"
|
|
68
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests",
|
|
69
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
72
70
|
}
|
|
73
71
|
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.morphTsSource = morphTsSource;
|
|
4
|
-
exports.configFromSourceFile = configFromSourceFile;
|
|
5
|
-
const ts_morph_1 = require("ts-morph");
|
|
6
|
-
const project = new ts_morph_1.Project({
|
|
7
|
-
tsConfigFilePath: "tsconfig.json",
|
|
8
|
-
skipAddingFilesFromTsConfig: true,
|
|
9
|
-
});
|
|
10
|
-
function morphTsSource(path) {
|
|
11
|
-
let tsSource = project.getSourceFile(path);
|
|
12
|
-
if (!tsSource)
|
|
13
|
-
tsSource = project.addSourceFileAtPath(path);
|
|
14
|
-
return tsSource;
|
|
15
|
-
}
|
|
16
|
-
const supportedImportPaths = [
|
|
17
|
-
"[type=grid].columns.filterOperators",
|
|
18
|
-
"[type=grid].columns.block",
|
|
19
|
-
"[type=grid].columns.component",
|
|
20
|
-
// TODO implement in generator "[type=grid].columns.renderCell",
|
|
21
|
-
//support in up to 5 levels of nested fields (eg. fieldSet)
|
|
22
|
-
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`),
|
|
23
|
-
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.block`),
|
|
24
|
-
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.component`),
|
|
25
|
-
];
|
|
26
|
-
const supportedInlineCodePaths = [
|
|
27
|
-
// TODO implement in generator "[type=grid].columns.filterOperators",
|
|
28
|
-
"[type=grid].columns.renderCell",
|
|
29
|
-
//support in up to 5 levels of nested fields (eg. fieldSet)
|
|
30
|
-
...Array.from(Array(5).keys()).map((i) => `[type=form]${".fields".repeat(i + 1)}.validate`),
|
|
31
|
-
];
|
|
32
|
-
function configFromSourceFile(sourceFile) {
|
|
33
|
-
for (const [name, declarations] of Array.from(sourceFile.getExportedDeclarations().entries())) {
|
|
34
|
-
if (name == "default") {
|
|
35
|
-
if (declarations.length != 1) {
|
|
36
|
-
throw new Error(`Expected exactly one declaration for ${name}`);
|
|
37
|
-
}
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
-
const config = exportedDeclarationToJson(declarations[0]);
|
|
40
|
-
//console.dir(config, { depth: 10 });
|
|
41
|
-
return config;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
//ignore this export
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
throw new Error(`No default export found, please export the GeneratorConfig as default, preferrable using defineConfig helper.`);
|
|
48
|
-
}
|
|
49
|
-
function findUsedImports(node) {
|
|
50
|
-
const imports = [];
|
|
51
|
-
node.getDescendantsOfKind(ts_morph_1.SyntaxKind.Identifier).forEach((identifier) => {
|
|
52
|
-
var _a;
|
|
53
|
-
for (const referencedSymbol of identifier.findReferences()) {
|
|
54
|
-
for (const reference of referencedSymbol.getReferences()) {
|
|
55
|
-
const referenceNode = reference.getNode();
|
|
56
|
-
const importDeclaration = referenceNode.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration);
|
|
57
|
-
if (importDeclaration) {
|
|
58
|
-
for (const namedImport of importDeclaration.getNamedImports()) {
|
|
59
|
-
if (((_a = (namedImport.getAliasNode() || namedImport.getNameNode())) === null || _a === void 0 ? void 0 : _a.getText()) == referenceNode.getText()) {
|
|
60
|
-
imports.push({
|
|
61
|
-
name: namedImport.getNameNode().getText(),
|
|
62
|
-
import: importDeclaration.getModuleSpecifierValue(),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
return imports;
|
|
71
|
-
}
|
|
72
|
-
function getTypePropertyFromObjectLiteral(node) {
|
|
73
|
-
for (const property of node.getProperties()) {
|
|
74
|
-
if (property.getKind() == ts_morph_1.SyntaxKind.PropertyAssignment) {
|
|
75
|
-
const propertyAssignment = property;
|
|
76
|
-
if (propertyAssignment.getName() == "type") {
|
|
77
|
-
const propertyAssignmentInitializer = propertyAssignment.getInitializer();
|
|
78
|
-
if ((propertyAssignmentInitializer === null || propertyAssignmentInitializer === void 0 ? void 0 : propertyAssignmentInitializer.getKind()) == ts_morph_1.SyntaxKind.StringLiteral) {
|
|
79
|
-
return propertyAssignmentInitializer.getLiteralValue();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
-
function astNodeToJson(node, path) {
|
|
88
|
-
var _a;
|
|
89
|
-
if (node.getKind() == ts_morph_1.SyntaxKind.ObjectLiteralExpression) {
|
|
90
|
-
if (path == "") {
|
|
91
|
-
// first entry of path is the type, then property names (. separated) are added
|
|
92
|
-
const typeProperty = getTypePropertyFromObjectLiteral(node);
|
|
93
|
-
path = typeProperty ? `[type=${typeProperty}]` : "";
|
|
94
|
-
}
|
|
95
|
-
const ret = {};
|
|
96
|
-
for (const property of node.getProperties()) {
|
|
97
|
-
if (property.getKind() == ts_morph_1.SyntaxKind.PropertyAssignment) {
|
|
98
|
-
const propertyAssignment = property;
|
|
99
|
-
const propertyAssignmentInitializer = propertyAssignment.getInitializer();
|
|
100
|
-
if (propertyAssignmentInitializer) {
|
|
101
|
-
ret[propertyAssignment.getName()] = astNodeToJson(propertyAssignmentInitializer, `${path}.${propertyAssignment.getName()}`);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
throw new Error(`Initializer is required for propertyAssignment '${propertyAssignment.getName()}'`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
throw new Error(`Unsupported ObjectLiteralExpression property Kind ${property.getKindName()}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return ret;
|
|
112
|
-
}
|
|
113
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.StringLiteral) {
|
|
114
|
-
return node.getLiteralValue();
|
|
115
|
-
}
|
|
116
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.NumericLiteral) {
|
|
117
|
-
return node.getLiteralValue();
|
|
118
|
-
}
|
|
119
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.FalseKeyword) {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.TrueKeyword) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.BinaryExpression) {
|
|
126
|
-
//what is this?
|
|
127
|
-
return node.getText();
|
|
128
|
-
}
|
|
129
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.ArrowFunction) {
|
|
130
|
-
if (supportedInlineCodePaths.includes(path)) {
|
|
131
|
-
const body = node.getBody();
|
|
132
|
-
return {
|
|
133
|
-
code: node.getText(),
|
|
134
|
-
imports: findUsedImports(body),
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
throw new Error(`Inline Function is not allowed here and calling the function is not supported: ${path}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.ArrayLiteralExpression) {
|
|
142
|
-
const ret = [];
|
|
143
|
-
for (const element of node.getElements()) {
|
|
144
|
-
ret.push(astNodeToJson(element, path));
|
|
145
|
-
}
|
|
146
|
-
return ret;
|
|
147
|
-
}
|
|
148
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.Identifier) {
|
|
149
|
-
for (const referencedSymbol of node.findReferences()) {
|
|
150
|
-
for (const reference of referencedSymbol.getReferences()) {
|
|
151
|
-
const referenceNode = reference.getNode();
|
|
152
|
-
const importDeclaration = referenceNode.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ImportDeclaration);
|
|
153
|
-
const variableDeclaration = referenceNode.getFirstAncestorByKind(ts_morph_1.SyntaxKind.VariableDeclaration);
|
|
154
|
-
if (importDeclaration) {
|
|
155
|
-
for (const namedImport of importDeclaration.getNamedImports()) {
|
|
156
|
-
if (((_a = (namedImport.getAliasNode() || namedImport.getNameNode())) === null || _a === void 0 ? void 0 : _a.getText()) == referenceNode.getText()) {
|
|
157
|
-
if (supportedImportPaths.includes(path)) {
|
|
158
|
-
return {
|
|
159
|
-
name: namedImport.getNameNode().getText(),
|
|
160
|
-
import: importDeclaration.getModuleSpecifierValue(),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
throw new Error(`Following the import is not supported: ${path} ${namedImport.getText()}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else if (variableDeclaration) {
|
|
170
|
-
if (variableDeclaration.getName() == node.getText()) {
|
|
171
|
-
const variableInitializer = variableDeclaration.getInitializer();
|
|
172
|
-
if (variableInitializer) {
|
|
173
|
-
return astNodeToJson(variableInitializer, path);
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
throw new Error(`Initializer is required for variableDeclaration '${variableDeclaration.getName()}'`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
throw new Error(`Unsupported identifier '${node.getText()}', only imports and are variable declarations with initializer are supported`);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
throw new Error(`Unsupported expression Kind ${node.getKindName()}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
189
|
-
function exportedDeclarationToJson(node) {
|
|
190
|
-
if (node.getKind() == ts_morph_1.SyntaxKind.VariableDeclaration) {
|
|
191
|
-
const variableDeclaration = node;
|
|
192
|
-
const initializer = variableDeclaration.getInitializer();
|
|
193
|
-
if (initializer) {
|
|
194
|
-
return astNodeToJson(initializer, "");
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
throw new Error(`Initializer is required for variableDeclaration '${variableDeclaration.getName()}'`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (node.getKind() == ts_morph_1.SyntaxKind.CallExpression) {
|
|
201
|
-
const callExpression = node;
|
|
202
|
-
if (callExpression.getExpression().getText() == "defineConfig") {
|
|
203
|
-
const args = callExpression.getArguments();
|
|
204
|
-
if (args.length != 1) {
|
|
205
|
-
throw new Error(`Expected exactly one argument for defineConfig`);
|
|
206
|
-
}
|
|
207
|
-
return astNodeToJson(args[0], "");
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
throw new Error(`Only direct call to defineConfig is supported`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
throw new Error(`Unsupported declaration kind '${node.getKindName()}'`);
|
|
215
|
-
}
|
|
216
|
-
}
|