@ostack.tech/kform-scaffolder 0.31.3 → 0.31.4
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.
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useDroppable, useDraggable, useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, closestCenter, DragOverlay } from "@dnd-kit/core";
|
|
3
|
-
import {
|
|
3
|
+
import { createContext, useContext, useCallback, useMemo, useRef, useLayoutEffect as useLayoutEffect$1, useState } from "react";
|
|
4
4
|
import { create } from "zustand";
|
|
5
5
|
import { persist } from "zustand/middleware";
|
|
6
6
|
import { fileOpen, fileSave, supported } from "browser-fs-access";
|
|
7
|
-
import { pascalCase, camelCase } from "change-case";
|
|
8
7
|
import JSZip from "jszip";
|
|
9
8
|
import { render } from "ejs";
|
|
10
9
|
function createSchematic(schematic) {
|
|
@@ -64,6 +63,258 @@ function add(schematic, toAdd, parentId, sortBefore) {
|
|
|
64
63
|
)
|
|
65
64
|
};
|
|
66
65
|
}
|
|
66
|
+
const SchematicBuilderContext = createContext(null);
|
|
67
|
+
function useSchematicBuilderContext() {
|
|
68
|
+
const context = useContext(SchematicBuilderContext);
|
|
69
|
+
if (context === null) {
|
|
70
|
+
throw new Error("Schematic builder not in context.");
|
|
71
|
+
}
|
|
72
|
+
return context;
|
|
73
|
+
}
|
|
74
|
+
function LoadSchematic() {
|
|
75
|
+
const { name, setSchematic, latestSchematicFileHandleRef } = useSchematicBuilderContext();
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
"button",
|
|
78
|
+
{
|
|
79
|
+
className: "builder-icon-button builder-action builder-load-schematic",
|
|
80
|
+
type: "button",
|
|
81
|
+
onClick: async () => {
|
|
82
|
+
try {
|
|
83
|
+
const file = await fileOpen({
|
|
84
|
+
extensions: [".json"],
|
|
85
|
+
mimeTypes: ["application/json"],
|
|
86
|
+
description: "Form schematic",
|
|
87
|
+
id: name
|
|
88
|
+
});
|
|
89
|
+
if (file.handle) {
|
|
90
|
+
latestSchematicFileHandleRef.current = file.handle;
|
|
91
|
+
}
|
|
92
|
+
const content = JSON.parse(await file.text());
|
|
93
|
+
setSchematic(() => content);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
alert(`Unable to load schematic: ${err?.toString()}`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
title: "Load schematic",
|
|
102
|
+
"aria-label": "Load schematic",
|
|
103
|
+
children: "📂"
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
function SaveSchematic() {
|
|
108
|
+
const { name, getRootSchematic, latestSchematicFileHandleRef } = useSchematicBuilderContext();
|
|
109
|
+
return /* @__PURE__ */ jsx(
|
|
110
|
+
"button",
|
|
111
|
+
{
|
|
112
|
+
className: "builder-icon-button builder-action builder-save-schematic",
|
|
113
|
+
type: "button",
|
|
114
|
+
onClick: async () => {
|
|
115
|
+
try {
|
|
116
|
+
const schematic = getRootSchematic();
|
|
117
|
+
latestSchematicFileHandleRef.current = await fileSave(
|
|
118
|
+
new Blob([JSON.stringify(schematic, null, 2)]),
|
|
119
|
+
{
|
|
120
|
+
fileName: `${schematic.name}-schematic.json`,
|
|
121
|
+
extensions: [".json"],
|
|
122
|
+
mimeTypes: ["application/json"],
|
|
123
|
+
description: "Form schematic",
|
|
124
|
+
id: name
|
|
125
|
+
},
|
|
126
|
+
latestSchematicFileHandleRef.current
|
|
127
|
+
);
|
|
128
|
+
if (supported) {
|
|
129
|
+
alert(
|
|
130
|
+
`Schematic saved successfully as “${latestSchematicFileHandleRef.current.name}”.`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
alert(`Unable to save schematic: ${err?.toString()}`);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
title: "Save schematic",
|
|
141
|
+
"aria-label": "Save schematic",
|
|
142
|
+
children: "💾"
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function configScaffolder(scaffolder, data) {
|
|
147
|
+
return (schematic, originalData) => scaffolder(schematic, { ...originalData, ...data });
|
|
148
|
+
}
|
|
149
|
+
function join(s1, s2, separator) {
|
|
150
|
+
return !s1 || !s2 ? s1 || s2 || "" : `${s1}${s1.endsWith(separator) ? "" : separator}${s2}`;
|
|
151
|
+
}
|
|
152
|
+
function joinPaths(path1, path2) {
|
|
153
|
+
return join(path1, path2, "/");
|
|
154
|
+
}
|
|
155
|
+
function joinPackages(pkg1, pkg2) {
|
|
156
|
+
return join(pkg1, pkg2, ".");
|
|
157
|
+
}
|
|
158
|
+
function scaffold(schematic, scaffolders, options) {
|
|
159
|
+
const files = /* @__PURE__ */ new Map();
|
|
160
|
+
const data = {
|
|
161
|
+
schematicKinds: options.schematicKinds,
|
|
162
|
+
rootSchematic: schematic,
|
|
163
|
+
files,
|
|
164
|
+
currentPath: options.basePath ?? "/",
|
|
165
|
+
currentPackage: joinPackages(options.basePackage, schematic.packageSuffix),
|
|
166
|
+
currentDir: options.baseDir
|
|
167
|
+
};
|
|
168
|
+
for (const scaffolder of scaffolders) {
|
|
169
|
+
scaffolder(schematic, data);
|
|
170
|
+
}
|
|
171
|
+
return Array.from(files).map(([path, file]) => ({
|
|
172
|
+
path,
|
|
173
|
+
content: file.getContent(),
|
|
174
|
+
base64: file.base64,
|
|
175
|
+
binary: file.binary,
|
|
176
|
+
executable: file.executable
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
function useScaffolder() {
|
|
180
|
+
const {
|
|
181
|
+
basePath,
|
|
182
|
+
basePackage,
|
|
183
|
+
baseDir,
|
|
184
|
+
schematicKinds,
|
|
185
|
+
scaffolders,
|
|
186
|
+
scaffoldingData,
|
|
187
|
+
getRootSchematic
|
|
188
|
+
} = useSchematicBuilderContext();
|
|
189
|
+
return useCallback(() => {
|
|
190
|
+
const schematic = getRootSchematic();
|
|
191
|
+
let actualScaffolders = typeof scaffolders === "function" ? scaffolders(schematic) : scaffolders;
|
|
192
|
+
const actualScaffoldingData = typeof scaffoldingData === "function" ? scaffoldingData(schematic) : scaffoldingData;
|
|
193
|
+
if (actualScaffoldingData) {
|
|
194
|
+
actualScaffolders = actualScaffolders.map(
|
|
195
|
+
(scaffolder) => configScaffolder(scaffolder, actualScaffoldingData)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return scaffold(schematic, actualScaffolders, {
|
|
199
|
+
schematicKinds,
|
|
200
|
+
basePath,
|
|
201
|
+
basePackage,
|
|
202
|
+
baseDir: typeof baseDir === "function" ? baseDir(schematic) : baseDir
|
|
203
|
+
});
|
|
204
|
+
}, [
|
|
205
|
+
baseDir,
|
|
206
|
+
basePackage,
|
|
207
|
+
basePath,
|
|
208
|
+
getRootSchematic,
|
|
209
|
+
scaffolders,
|
|
210
|
+
scaffoldingData,
|
|
211
|
+
schematicKinds
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
let latestZipFileHandle = null;
|
|
215
|
+
function ScaffoldToZip() {
|
|
216
|
+
const { getRootSchematic, name } = useSchematicBuilderContext();
|
|
217
|
+
const scaffold2 = useScaffolder();
|
|
218
|
+
return /* @__PURE__ */ jsx(
|
|
219
|
+
"button",
|
|
220
|
+
{
|
|
221
|
+
className: "builder-input builder-action builder-scaffold-zip",
|
|
222
|
+
type: "submit",
|
|
223
|
+
onClick: async (evt) => {
|
|
224
|
+
evt.preventDefault();
|
|
225
|
+
try {
|
|
226
|
+
const schematic = getRootSchematic();
|
|
227
|
+
const files = scaffold2();
|
|
228
|
+
const zip = new JSZip();
|
|
229
|
+
for (const { path, content, base64, binary, executable } of files) {
|
|
230
|
+
zip.file(path, content, {
|
|
231
|
+
base64,
|
|
232
|
+
binary,
|
|
233
|
+
unixPermissions: executable ? "755" : void 0
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
latestZipFileHandle = await fileSave(
|
|
237
|
+
zip.generateAsync({ platform: "UNIX", type: "blob" }),
|
|
238
|
+
{
|
|
239
|
+
fileName: `${schematic.name}.zip`,
|
|
240
|
+
extensions: [".zip"],
|
|
241
|
+
mimeTypes: ["application/zip"],
|
|
242
|
+
description: "Form scaffolding",
|
|
243
|
+
id: name
|
|
244
|
+
},
|
|
245
|
+
latestZipFileHandle
|
|
246
|
+
);
|
|
247
|
+
if (supported) {
|
|
248
|
+
alert(
|
|
249
|
+
`Project scaffolded successfully in “${latestZipFileHandle.name}”.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
} catch (err) {
|
|
253
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
alert(`Unable to scaffold project: ${err?.toString()}`);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
children: "Scaffold 🏗️"
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
function useConfig(configName, defaultValue) {
|
|
264
|
+
const { useSchematic, setSchematic } = useSchematicBuilderContext();
|
|
265
|
+
const configValue = useSchematic(
|
|
266
|
+
(schematic) => schematic.config?.[configName] ?? defaultValue
|
|
267
|
+
);
|
|
268
|
+
const setConfigValue = useCallback(
|
|
269
|
+
(configValue2) => setSchematic((schematic) => ({
|
|
270
|
+
config: {
|
|
271
|
+
...schematic.config,
|
|
272
|
+
[configName]: typeof configValue2 === "function" ? configValue2(schematic.config?.[configName]) : configValue2
|
|
273
|
+
}
|
|
274
|
+
})),
|
|
275
|
+
[configName, setSchematic]
|
|
276
|
+
);
|
|
277
|
+
return useMemo(
|
|
278
|
+
() => [configValue, setConfigValue],
|
|
279
|
+
[configValue, setConfigValue]
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
function UseFileBase64SerializerConfig({
|
|
283
|
+
disabled
|
|
284
|
+
}) {
|
|
285
|
+
const [config, setConfig] = useConfig("useFileBase64Serializer", false);
|
|
286
|
+
return /* @__PURE__ */ jsx("div", { className: "builder-config-field", children: /* @__PURE__ */ jsxs("label", { children: [
|
|
287
|
+
/* @__PURE__ */ jsx(
|
|
288
|
+
"input",
|
|
289
|
+
{
|
|
290
|
+
type: "checkbox",
|
|
291
|
+
checked: config,
|
|
292
|
+
onChange: (evt) => setConfig(evt.target.checked),
|
|
293
|
+
disabled
|
|
294
|
+
}
|
|
295
|
+
),
|
|
296
|
+
"Use ",
|
|
297
|
+
/* @__PURE__ */ jsx("code", { children: "File.Base64Serializer" })
|
|
298
|
+
] }) });
|
|
299
|
+
}
|
|
300
|
+
function UseTableValuesSerializerConfig({
|
|
301
|
+
disabled
|
|
302
|
+
}) {
|
|
303
|
+
const [config, setConfig] = useConfig("useTableValuesSerializer", false);
|
|
304
|
+
return /* @__PURE__ */ jsx("div", { className: "builder-config-field", children: /* @__PURE__ */ jsxs("label", { children: [
|
|
305
|
+
/* @__PURE__ */ jsx(
|
|
306
|
+
"input",
|
|
307
|
+
{
|
|
308
|
+
type: "checkbox",
|
|
309
|
+
checked: config,
|
|
310
|
+
onChange: (evt) => setConfig(evt.target.checked),
|
|
311
|
+
disabled
|
|
312
|
+
}
|
|
313
|
+
),
|
|
314
|
+
"Use ",
|
|
315
|
+
/* @__PURE__ */ jsx("code", { children: "Table.ValuesSerializer" })
|
|
316
|
+
] }) });
|
|
317
|
+
}
|
|
67
318
|
const DEFAULT_IMPORTS = [
|
|
68
319
|
/^kotlin\.[^.]+$/,
|
|
69
320
|
/^kotlin\.annotation\.[^.]+$/,
|
|
@@ -117,15 +368,6 @@ function serializable(code2, data) {
|
|
|
117
368
|
data.currentFile.imports.add(SERIALIZABLE);
|
|
118
369
|
return annotate(`@${simpleKtName(SERIALIZABLE)}`, code2);
|
|
119
370
|
}
|
|
120
|
-
function join(s1, s2, separator) {
|
|
121
|
-
return !s1 || !s2 ? s1 || s2 || "" : `${s1}${s1.endsWith(separator) ? "" : separator}${s2}`;
|
|
122
|
-
}
|
|
123
|
-
function joinPaths(path1, path2) {
|
|
124
|
-
return join(path1, path2, "/");
|
|
125
|
-
}
|
|
126
|
-
function joinPackages(pkg1, pkg2) {
|
|
127
|
-
return join(pkg1, pkg2, ".");
|
|
128
|
-
}
|
|
129
371
|
function scaffoldModels(schematic, data) {
|
|
130
372
|
const fileName = joinPaths(data.currentDir, `${schematic.name}.kt`);
|
|
131
373
|
let file = data.files.get(fileName);
|
|
@@ -185,59 +427,93 @@ function scaffoldDefaultValue(schematic, data) {
|
|
|
185
427
|
}
|
|
186
428
|
return schematic.nullable ? "null" : typeof schematicKind.defaultValue === "function" ? schematicKind.defaultValue(schematic, data) : schematicKind.defaultValue;
|
|
187
429
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (
|
|
203
|
-
|
|
430
|
+
const SPLIT_LOWER_UPPER_RE = new RegExp("([\\p{Ll}\\d])(\\p{Lu})", "gu");
|
|
431
|
+
const SPLIT_UPPER_UPPER_RE = new RegExp("(\\p{Lu})([\\p{Lu}][\\p{Ll}])", "gu");
|
|
432
|
+
const SPLIT_SEPARATE_NUMBER_RE = new RegExp("(\\d)\\p{Ll}|(\\p{L})\\d", "u");
|
|
433
|
+
const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
|
|
434
|
+
const SPLIT_REPLACE_VALUE = "$1\0$2";
|
|
435
|
+
const DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
|
|
436
|
+
function split(value) {
|
|
437
|
+
let result = value.trim();
|
|
438
|
+
result = result.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE).replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
|
|
439
|
+
result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
|
|
440
|
+
let start = 0;
|
|
441
|
+
let end = result.length;
|
|
442
|
+
while (result.charAt(start) === "\0")
|
|
443
|
+
start++;
|
|
444
|
+
if (start === end)
|
|
445
|
+
return [];
|
|
446
|
+
while (result.charAt(end - 1) === "\0")
|
|
447
|
+
end--;
|
|
448
|
+
return result.slice(start, end).split(/\0/g);
|
|
449
|
+
}
|
|
450
|
+
function splitSeparateNumbers(value) {
|
|
451
|
+
const words = split(value);
|
|
452
|
+
for (let i = 0; i < words.length; i++) {
|
|
453
|
+
const word = words[i];
|
|
454
|
+
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
|
|
455
|
+
if (match) {
|
|
456
|
+
const offset = match.index + (match[1] ?? match[2]).length;
|
|
457
|
+
words.splice(i, 1, word.slice(0, offset), word.slice(offset));
|
|
458
|
+
}
|
|
204
459
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
460
|
+
return words;
|
|
461
|
+
}
|
|
462
|
+
function camelCase(input, options) {
|
|
463
|
+
const [prefix, words, suffix] = splitPrefixSuffix(input, options);
|
|
464
|
+
const lower = lowerFactory(options?.locale);
|
|
465
|
+
const upper = upperFactory(options?.locale);
|
|
466
|
+
const transform = pascalCaseTransformFactory(lower, upper);
|
|
467
|
+
return prefix + words.map((word, index) => {
|
|
468
|
+
if (index === 0)
|
|
469
|
+
return lower(word);
|
|
470
|
+
return transform(word, index);
|
|
471
|
+
}).join("") + suffix;
|
|
472
|
+
}
|
|
473
|
+
function pascalCase(input, options) {
|
|
474
|
+
const [prefix, words, suffix] = splitPrefixSuffix(input, options);
|
|
475
|
+
const lower = lowerFactory(options?.locale);
|
|
476
|
+
const upper = upperFactory(options?.locale);
|
|
477
|
+
const transform = pascalCaseTransformFactory(lower, upper);
|
|
478
|
+
return prefix + words.map(transform).join("") + suffix;
|
|
479
|
+
}
|
|
480
|
+
function lowerFactory(locale) {
|
|
481
|
+
return (input) => input.toLocaleLowerCase(locale);
|
|
482
|
+
}
|
|
483
|
+
function upperFactory(locale) {
|
|
484
|
+
return (input) => input.toLocaleUpperCase(locale);
|
|
485
|
+
}
|
|
486
|
+
function pascalCaseTransformFactory(lower, upper) {
|
|
487
|
+
return (word, index) => {
|
|
488
|
+
const char0 = word[0];
|
|
489
|
+
const initial = index > 0 && char0 >= "0" && char0 <= "9" ? "_" + char0 : upper(char0);
|
|
490
|
+
return initial + lower(word.slice(1));
|
|
491
|
+
};
|
|
210
492
|
}
|
|
211
|
-
function
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
...data,
|
|
223
|
-
currentPackage: joinPackages(
|
|
224
|
-
data.currentPackage,
|
|
225
|
-
schematic.packageSuffix
|
|
226
|
-
),
|
|
227
|
-
currentDir: joinPaths(
|
|
228
|
-
data.currentDir,
|
|
229
|
-
schematic.packageSuffix?.replace(".", "/")
|
|
230
|
-
)
|
|
231
|
-
});
|
|
232
|
-
} else {
|
|
233
|
-
scaffoldedSchema = schematicKind.scaffoldSchema?.(schematic, data) ?? `${simpleKtName(schematicKind.schema)}()`;
|
|
234
|
-
data.currentFile.imports.add(schematicKind.schema);
|
|
493
|
+
function splitPrefixSuffix(input, options = {}) {
|
|
494
|
+
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
|
|
495
|
+
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
496
|
+
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
497
|
+
let prefixIndex = 0;
|
|
498
|
+
let suffixIndex = input.length;
|
|
499
|
+
while (prefixIndex < input.length) {
|
|
500
|
+
const char = input.charAt(prefixIndex);
|
|
501
|
+
if (!prefixCharacters.includes(char))
|
|
502
|
+
break;
|
|
503
|
+
prefixIndex++;
|
|
235
504
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
505
|
+
while (suffixIndex > prefixIndex) {
|
|
506
|
+
const index = suffixIndex - 1;
|
|
507
|
+
const char = input.charAt(index);
|
|
508
|
+
if (!suffixCharacters.includes(char))
|
|
509
|
+
break;
|
|
510
|
+
suffixIndex = index;
|
|
239
511
|
}
|
|
240
|
-
return
|
|
512
|
+
return [
|
|
513
|
+
input.slice(0, prefixIndex),
|
|
514
|
+
splitFn(input.slice(prefixIndex, suffixIndex)),
|
|
515
|
+
input.slice(suffixIndex)
|
|
516
|
+
];
|
|
241
517
|
}
|
|
242
518
|
function boolDataAttr(condition) {
|
|
243
519
|
return condition ? "" : void 0;
|
|
@@ -898,438 +1174,239 @@ const classSchematicKind = {
|
|
|
898
1174
|
`,
|
|
899
1175
|
scaffoldSchema: (schematic, data) => code`
|
|
900
1176
|
ClassSchema {
|
|
901
|
-
${schematic.children?.map(
|
|
902
|
-
(childSchematic) => `${schematic.name}::${childSchematic.childName} { ${scaffoldSchema(
|
|
903
|
-
childSchematic,
|
|
904
|
-
{
|
|
905
|
-
...data,
|
|
906
|
-
currentPath: joinPaths(
|
|
907
|
-
data.currentPath,
|
|
908
|
-
childSchematic.childName
|
|
909
|
-
)
|
|
910
|
-
}
|
|
911
|
-
)} }`
|
|
912
|
-
).join("\n")}
|
|
913
|
-
}
|
|
914
|
-
`
|
|
915
|
-
};
|
|
916
|
-
const doubleSchematicKind = {
|
|
917
|
-
kind: "Double",
|
|
918
|
-
package: "kotlin",
|
|
919
|
-
name: "Double",
|
|
920
|
-
schema: "tech.ostack.kform.schemas.DoubleSchema",
|
|
921
|
-
defaultNullable: true,
|
|
922
|
-
defaultValue: "0.0"
|
|
923
|
-
};
|
|
924
|
-
const enumSchematicKind = {
|
|
925
|
-
kind: "Enum",
|
|
926
|
-
schema: "tech.ostack.kform.schemas.EnumSchema",
|
|
927
|
-
defaultNullable: true,
|
|
928
|
-
defaultValue: (schematic) => `${schematic.name}.${schematic.children?.[0].childName}`,
|
|
929
|
-
builder: EnumBuilder,
|
|
930
|
-
initChildren: () => [],
|
|
931
|
-
defaultName: (childName) => pascalCase(childName),
|
|
932
|
-
scaffoldModel: (schematic) => code`
|
|
933
|
-
enum class ${schematic.name} {
|
|
934
|
-
${schematic.children?.map((childSchematic) => childSchematic.childName).join(",\n")}
|
|
935
|
-
}
|
|
936
|
-
`
|
|
937
|
-
};
|
|
938
|
-
const fileSchematicKind = {
|
|
939
|
-
kind: "File",
|
|
940
|
-
package: "tech.ostack.kform.datatypes",
|
|
941
|
-
name: "File",
|
|
942
|
-
schema: "tech.ostack.kform.schemas.FileSchema",
|
|
943
|
-
defaultNullable: true,
|
|
944
|
-
defaultValue: (_schematic, data) => {
|
|
945
|
-
data.currentFile.imports.add(
|
|
946
|
-
"tech.ostack.kform.datatypes.emptyPlaceholderFile"
|
|
947
|
-
);
|
|
948
|
-
return "emptyPlaceholderFile()";
|
|
949
|
-
},
|
|
950
|
-
propertyAnnotations: (_schematic, data) => data.useFileBase64Serializer ? "@Serializable(with = File.Base64Serializer::class)" : void 0
|
|
951
|
-
};
|
|
952
|
-
const floatSchematicKind = {
|
|
953
|
-
kind: "Float",
|
|
954
|
-
package: "kotlin",
|
|
955
|
-
name: "Float",
|
|
956
|
-
schema: "tech.ostack.kform.schemas.FloatSchema",
|
|
957
|
-
defaultNullable: true,
|
|
958
|
-
defaultValue: "0f"
|
|
959
|
-
};
|
|
960
|
-
const instantSchematicKind = {
|
|
961
|
-
kind: "Instant",
|
|
962
|
-
package: "kotlin.time",
|
|
963
|
-
name: "Instant",
|
|
964
|
-
schema: "tech.ostack.kform.schemas.InstantSchema",
|
|
965
|
-
defaultNullable: true,
|
|
966
|
-
defaultValue: "Instant.fromEpochMilliseconds(0)",
|
|
967
|
-
propertyAnnotations: (_schematic, data) => {
|
|
968
|
-
data.currentFile.imports.add("kotlin.OptIn").add("kotlin.time.ExperimentalTime");
|
|
969
|
-
return "@OptIn(ExperimentalTime::class)";
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
const intSchematicKind = {
|
|
973
|
-
kind: "Int",
|
|
974
|
-
package: "kotlin",
|
|
975
|
-
name: "Int",
|
|
976
|
-
schema: "tech.ostack.kform.schemas.IntSchema",
|
|
977
|
-
defaultNullable: true,
|
|
978
|
-
defaultValue: "0"
|
|
979
|
-
};
|
|
980
|
-
const listSchematicKind = {
|
|
981
|
-
kind: "List",
|
|
982
|
-
package: "kotlin.collections",
|
|
983
|
-
name: "List",
|
|
984
|
-
schema: "tech.ostack.kform.schemas.ListSchema",
|
|
985
|
-
defaultValue: "emptyList()",
|
|
986
|
-
builder: ListableBuilder,
|
|
987
|
-
initChildren: () => [createSchematic()],
|
|
988
|
-
scaffoldType: (schematic, data) => `List<${scaffoldType(schematic.children[0], {
|
|
989
|
-
...data,
|
|
990
|
-
currentPath: joinPaths(data.currentPath, "*")
|
|
991
|
-
})}>`,
|
|
992
|
-
scaffoldSchema: (schematic, data) => `ListSchema { ${scaffoldSchema(schematic.children[0], {
|
|
993
|
-
...data,
|
|
994
|
-
currentPath: joinPaths(data.currentPath, "*")
|
|
995
|
-
})} }`
|
|
996
|
-
};
|
|
997
|
-
const localDateSchematicKind = {
|
|
998
|
-
kind: "LocalDate",
|
|
999
|
-
package: "kotlinx.datetime",
|
|
1000
|
-
name: "LocalDate",
|
|
1001
|
-
schema: "tech.ostack.kform.schemas.LocalDateSchema",
|
|
1002
|
-
defaultNullable: true,
|
|
1003
|
-
defaultValue: "LocalDate(1970, 1, 1)"
|
|
1004
|
-
};
|
|
1005
|
-
const localDateTimeSchematicKind = {
|
|
1006
|
-
kind: "LocalDateTime",
|
|
1007
|
-
package: "kotlinx.datetime",
|
|
1008
|
-
name: "LocalDateTime",
|
|
1009
|
-
schema: "tech.ostack.kform.schemas.LocalDateTimeSchema",
|
|
1010
|
-
defaultNullable: true,
|
|
1011
|
-
defaultValue: "LocalDateTime(1970, 1, 1, 0, 0)"
|
|
1012
|
-
};
|
|
1013
|
-
const longSchematicKind = {
|
|
1014
|
-
kind: "Long",
|
|
1015
|
-
package: "kotlin",
|
|
1016
|
-
name: "Long",
|
|
1017
|
-
schema: "tech.ostack.kform.schemas.LongSchema",
|
|
1018
|
-
defaultNullable: true,
|
|
1019
|
-
defaultValue: "0L"
|
|
1020
|
-
};
|
|
1021
|
-
const shortSchematicKind = {
|
|
1022
|
-
kind: "Short",
|
|
1023
|
-
package: "kotlin",
|
|
1024
|
-
name: "Short",
|
|
1025
|
-
schema: "tech.ostack.kform.schemas.ShortSchema",
|
|
1026
|
-
defaultNullable: true,
|
|
1027
|
-
defaultValue: "0.toShort()"
|
|
1028
|
-
};
|
|
1029
|
-
const stringSchematicKind = {
|
|
1030
|
-
kind: "String",
|
|
1031
|
-
package: "kotlin",
|
|
1032
|
-
name: "String",
|
|
1033
|
-
schema: "tech.ostack.kform.schemas.StringSchema",
|
|
1034
|
-
defaultValue: '""'
|
|
1035
|
-
};
|
|
1036
|
-
const tableSchematicKind = {
|
|
1037
|
-
kind: "Table",
|
|
1038
|
-
package: "tech.ostack.kform.datatypes",
|
|
1039
|
-
name: "Table",
|
|
1040
|
-
schema: "tech.ostack.kform.schemas.TableSchema",
|
|
1041
|
-
defaultValue: (_schematic, data) => {
|
|
1042
|
-
data.currentFile.imports.add("tech.ostack.kform.datatypes.tableOf");
|
|
1043
|
-
return "tableOf()";
|
|
1044
|
-
},
|
|
1045
|
-
builder: ListableBuilder,
|
|
1046
|
-
initChildren: () => [createSchematic()],
|
|
1047
|
-
propertyAnnotations: (_schematic, data) => data.useTableValuesSerializer ? "@Serializable(with = Table.ValuesSerializer::class)" : void 0,
|
|
1048
|
-
scaffoldType: (schematic, data) => `Table<${scaffoldType(schematic.children[0], {
|
|
1049
|
-
...data,
|
|
1050
|
-
currentPath: joinPaths(data.currentPath, "*")
|
|
1051
|
-
})}>`,
|
|
1052
|
-
scaffoldSchema: (schematic, data) => `TableSchema { ${scaffoldSchema(schematic.children[0], {
|
|
1053
|
-
...data,
|
|
1054
|
-
currentPath: joinPaths(data.currentPath, "*")
|
|
1055
|
-
})} }`
|
|
1056
|
-
};
|
|
1057
|
-
const defaultSchematicKinds = [
|
|
1058
|
-
anySchematicKind,
|
|
1059
|
-
bigDecimalSchematicKind,
|
|
1060
|
-
bigIntegerSchematicKind,
|
|
1061
|
-
booleanSchematicKind,
|
|
1062
|
-
byteSchematicKind,
|
|
1063
|
-
charSchematicKind,
|
|
1064
|
-
classSchematicKind,
|
|
1065
|
-
doubleSchematicKind,
|
|
1066
|
-
enumSchematicKind,
|
|
1067
|
-
fileSchematicKind,
|
|
1068
|
-
floatSchematicKind,
|
|
1069
|
-
instantSchematicKind,
|
|
1070
|
-
intSchematicKind,
|
|
1071
|
-
listSchematicKind,
|
|
1072
|
-
localDateSchematicKind,
|
|
1073
|
-
localDateTimeSchematicKind,
|
|
1074
|
-
longSchematicKind,
|
|
1075
|
-
shortSchematicKind,
|
|
1076
|
-
stringSchematicKind,
|
|
1077
|
-
tableSchematicKind
|
|
1078
|
-
];
|
|
1079
|
-
function scaffold(schematic, scaffolders, options) {
|
|
1080
|
-
const files = /* @__PURE__ */ new Map();
|
|
1081
|
-
const data = {
|
|
1082
|
-
schematicKinds: options.schematicKinds,
|
|
1083
|
-
rootSchematic: schematic,
|
|
1084
|
-
files,
|
|
1085
|
-
currentPath: options.basePath ?? "/",
|
|
1086
|
-
currentPackage: joinPackages(options.basePackage, schematic.packageSuffix),
|
|
1087
|
-
currentDir: options.baseDir
|
|
1088
|
-
};
|
|
1089
|
-
for (const scaffolder of scaffolders) {
|
|
1090
|
-
scaffolder(schematic, data);
|
|
1091
|
-
}
|
|
1092
|
-
return Array.from(files).map(([path, file]) => ({
|
|
1093
|
-
path,
|
|
1094
|
-
content: file.getContent(),
|
|
1095
|
-
base64: file.base64,
|
|
1096
|
-
binary: file.binary,
|
|
1097
|
-
executable: file.executable
|
|
1098
|
-
}));
|
|
1099
|
-
}
|
|
1100
|
-
const SchematicBuilderContext = createContext(null);
|
|
1101
|
-
function useSchematicBuilderContext() {
|
|
1102
|
-
const context = useContext(SchematicBuilderContext);
|
|
1103
|
-
if (context === null) {
|
|
1104
|
-
throw new Error("Schematic builder not in context.");
|
|
1105
|
-
}
|
|
1106
|
-
return context;
|
|
1107
|
-
}
|
|
1108
|
-
function LoadSchematic() {
|
|
1109
|
-
const { name, setSchematic, latestSchematicFileHandleRef } = useSchematicBuilderContext();
|
|
1110
|
-
return /* @__PURE__ */ jsx(
|
|
1111
|
-
"button",
|
|
1112
|
-
{
|
|
1113
|
-
className: "builder-icon-button builder-action builder-load-schematic",
|
|
1114
|
-
type: "button",
|
|
1115
|
-
onClick: async () => {
|
|
1116
|
-
try {
|
|
1117
|
-
const file = await fileOpen({
|
|
1118
|
-
extensions: [".json"],
|
|
1119
|
-
mimeTypes: ["application/json"],
|
|
1120
|
-
description: "Form schematic",
|
|
1121
|
-
id: name
|
|
1122
|
-
});
|
|
1123
|
-
if (file.handle) {
|
|
1124
|
-
latestSchematicFileHandleRef.current = file.handle;
|
|
1125
|
-
}
|
|
1126
|
-
const content = JSON.parse(await file.text());
|
|
1127
|
-
setSchematic(() => content);
|
|
1128
|
-
} catch (err) {
|
|
1129
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1130
|
-
return;
|
|
1131
|
-
}
|
|
1132
|
-
alert(`Unable to load schematic: ${err?.toString()}`);
|
|
1133
|
-
}
|
|
1134
|
-
},
|
|
1135
|
-
title: "Load schematic",
|
|
1136
|
-
"aria-label": "Load schematic",
|
|
1137
|
-
children: "📂"
|
|
1138
|
-
}
|
|
1139
|
-
);
|
|
1140
|
-
}
|
|
1141
|
-
function SaveSchematic() {
|
|
1142
|
-
const { name, getRootSchematic, latestSchematicFileHandleRef } = useSchematicBuilderContext();
|
|
1143
|
-
return /* @__PURE__ */ jsx(
|
|
1144
|
-
"button",
|
|
1145
|
-
{
|
|
1146
|
-
className: "builder-icon-button builder-action builder-save-schematic",
|
|
1147
|
-
type: "button",
|
|
1148
|
-
onClick: async () => {
|
|
1149
|
-
try {
|
|
1150
|
-
const schematic = getRootSchematic();
|
|
1151
|
-
latestSchematicFileHandleRef.current = await fileSave(
|
|
1152
|
-
new Blob([JSON.stringify(schematic, null, 2)]),
|
|
1153
|
-
{
|
|
1154
|
-
fileName: `${schematic.name}-schematic.json`,
|
|
1155
|
-
extensions: [".json"],
|
|
1156
|
-
mimeTypes: ["application/json"],
|
|
1157
|
-
description: "Form schematic",
|
|
1158
|
-
id: name
|
|
1159
|
-
},
|
|
1160
|
-
latestSchematicFileHandleRef.current
|
|
1161
|
-
);
|
|
1162
|
-
if (supported) {
|
|
1163
|
-
alert(
|
|
1164
|
-
`Schematic saved successfully as “${latestSchematicFileHandleRef.current.name}”.`
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
} catch (err) {
|
|
1168
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
alert(`Unable to save schematic: ${err?.toString()}`);
|
|
1172
|
-
}
|
|
1173
|
-
},
|
|
1174
|
-
title: "Save schematic",
|
|
1175
|
-
"aria-label": "Save schematic",
|
|
1176
|
-
children: "💾"
|
|
1177
|
-
}
|
|
1178
|
-
);
|
|
1179
|
-
}
|
|
1180
|
-
function configScaffolder(scaffolder, data) {
|
|
1181
|
-
return (schematic, originalData) => scaffolder(schematic, { ...originalData, ...data });
|
|
1182
|
-
}
|
|
1183
|
-
function useScaffolder() {
|
|
1184
|
-
const {
|
|
1185
|
-
basePath,
|
|
1186
|
-
basePackage,
|
|
1187
|
-
baseDir,
|
|
1188
|
-
schematicKinds,
|
|
1189
|
-
scaffolders,
|
|
1190
|
-
scaffoldingData,
|
|
1191
|
-
getRootSchematic
|
|
1192
|
-
} = useSchematicBuilderContext();
|
|
1193
|
-
return useCallback(() => {
|
|
1194
|
-
const schematic = getRootSchematic();
|
|
1195
|
-
let actualScaffolders = typeof scaffolders === "function" ? scaffolders(schematic) : scaffolders;
|
|
1196
|
-
const actualScaffoldingData = typeof scaffoldingData === "function" ? scaffoldingData(schematic) : scaffoldingData;
|
|
1197
|
-
if (actualScaffoldingData) {
|
|
1198
|
-
actualScaffolders = actualScaffolders.map(
|
|
1199
|
-
(scaffolder) => configScaffolder(scaffolder, actualScaffoldingData)
|
|
1200
|
-
);
|
|
1201
|
-
}
|
|
1202
|
-
return scaffold(schematic, actualScaffolders, {
|
|
1203
|
-
schematicKinds,
|
|
1204
|
-
basePath,
|
|
1205
|
-
basePackage,
|
|
1206
|
-
baseDir: typeof baseDir === "function" ? baseDir(schematic) : baseDir
|
|
1207
|
-
});
|
|
1208
|
-
}, [
|
|
1209
|
-
baseDir,
|
|
1210
|
-
basePackage,
|
|
1211
|
-
basePath,
|
|
1212
|
-
getRootSchematic,
|
|
1213
|
-
scaffolders,
|
|
1214
|
-
scaffoldingData,
|
|
1215
|
-
schematicKinds
|
|
1216
|
-
]);
|
|
1217
|
-
}
|
|
1218
|
-
let latestZipFileHandle = null;
|
|
1219
|
-
function ScaffoldToZip() {
|
|
1220
|
-
const { getRootSchematic, name } = useSchematicBuilderContext();
|
|
1221
|
-
const scaffold2 = useScaffolder();
|
|
1222
|
-
return /* @__PURE__ */ jsx(
|
|
1223
|
-
"button",
|
|
1224
|
-
{
|
|
1225
|
-
className: "builder-input builder-action builder-scaffold-zip",
|
|
1226
|
-
type: "submit",
|
|
1227
|
-
onClick: async (evt) => {
|
|
1228
|
-
evt.preventDefault();
|
|
1229
|
-
try {
|
|
1230
|
-
const schematic = getRootSchematic();
|
|
1231
|
-
const files = scaffold2();
|
|
1232
|
-
const zip = new JSZip();
|
|
1233
|
-
for (const { path, content, base64, binary, executable } of files) {
|
|
1234
|
-
zip.file(path, content, {
|
|
1235
|
-
base64,
|
|
1236
|
-
binary,
|
|
1237
|
-
unixPermissions: executable ? "755" : void 0
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
latestZipFileHandle = await fileSave(
|
|
1241
|
-
zip.generateAsync({ platform: "UNIX", type: "blob" }),
|
|
1242
|
-
{
|
|
1243
|
-
fileName: `${schematic.name}.zip`,
|
|
1244
|
-
extensions: [".zip"],
|
|
1245
|
-
mimeTypes: ["application/zip"],
|
|
1246
|
-
description: "Form scaffolding",
|
|
1247
|
-
id: name
|
|
1248
|
-
},
|
|
1249
|
-
latestZipFileHandle
|
|
1250
|
-
);
|
|
1251
|
-
if (supported) {
|
|
1252
|
-
alert(
|
|
1253
|
-
`Project scaffolded successfully in “${latestZipFileHandle.name}”.`
|
|
1254
|
-
);
|
|
1255
|
-
}
|
|
1256
|
-
} catch (err) {
|
|
1257
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1258
|
-
return;
|
|
1259
|
-
}
|
|
1260
|
-
alert(`Unable to scaffold project: ${err?.toString()}`);
|
|
1261
|
-
}
|
|
1262
|
-
},
|
|
1263
|
-
children: "Scaffold 🏗️"
|
|
1264
|
-
}
|
|
1265
|
-
);
|
|
1266
|
-
}
|
|
1267
|
-
function useConfig(configName, defaultValue) {
|
|
1268
|
-
const { useSchematic, setSchematic } = useSchematicBuilderContext();
|
|
1269
|
-
const configValue = useSchematic(
|
|
1270
|
-
(schematic) => schematic.config?.[configName] ?? defaultValue
|
|
1271
|
-
);
|
|
1272
|
-
const setConfigValue = useCallback(
|
|
1273
|
-
(configValue2) => setSchematic((schematic) => ({
|
|
1274
|
-
config: {
|
|
1275
|
-
...schematic.config,
|
|
1276
|
-
[configName]: typeof configValue2 === "function" ? configValue2(schematic.config?.[configName]) : configValue2
|
|
1277
|
-
}
|
|
1278
|
-
})),
|
|
1279
|
-
[configName, setSchematic]
|
|
1280
|
-
);
|
|
1281
|
-
return useMemo(
|
|
1282
|
-
() => [configValue, setConfigValue],
|
|
1283
|
-
[configValue, setConfigValue]
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
function UseFileBase64SerializerConfig({
|
|
1287
|
-
disabled
|
|
1288
|
-
}) {
|
|
1289
|
-
const [config, setConfig] = useConfig("useFileBase64Serializer", false);
|
|
1290
|
-
return /* @__PURE__ */ jsx("div", { className: "builder-config-field", children: /* @__PURE__ */ jsxs("label", { children: [
|
|
1291
|
-
/* @__PURE__ */ jsx(
|
|
1292
|
-
"input",
|
|
1293
|
-
{
|
|
1294
|
-
type: "checkbox",
|
|
1295
|
-
checked: config,
|
|
1296
|
-
onChange: (evt) => setConfig(evt.target.checked),
|
|
1297
|
-
disabled
|
|
1298
|
-
}
|
|
1299
|
-
),
|
|
1300
|
-
"Use ",
|
|
1301
|
-
/* @__PURE__ */ jsx("code", { children: "File.Base64Serializer" })
|
|
1302
|
-
] }) });
|
|
1303
|
-
}
|
|
1304
|
-
function UseTableValuesSerializerConfig({
|
|
1305
|
-
disabled
|
|
1306
|
-
}) {
|
|
1307
|
-
const [config, setConfig] = useConfig("useTableValuesSerializer", false);
|
|
1308
|
-
return /* @__PURE__ */ jsx("div", { className: "builder-config-field", children: /* @__PURE__ */ jsxs("label", { children: [
|
|
1309
|
-
/* @__PURE__ */ jsx(
|
|
1310
|
-
"input",
|
|
1177
|
+
${schematic.children?.map(
|
|
1178
|
+
(childSchematic) => `${schematic.name}::${childSchematic.childName} { ${scaffoldSchema(
|
|
1179
|
+
childSchematic,
|
|
1311
1180
|
{
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1181
|
+
...data,
|
|
1182
|
+
currentPath: joinPaths(
|
|
1183
|
+
data.currentPath,
|
|
1184
|
+
childSchematic.childName
|
|
1185
|
+
)
|
|
1316
1186
|
}
|
|
1317
|
-
)
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1187
|
+
)} }`
|
|
1188
|
+
).join("\n")}
|
|
1189
|
+
}
|
|
1190
|
+
`
|
|
1191
|
+
};
|
|
1192
|
+
const doubleSchematicKind = {
|
|
1193
|
+
kind: "Double",
|
|
1194
|
+
package: "kotlin",
|
|
1195
|
+
name: "Double",
|
|
1196
|
+
schema: "tech.ostack.kform.schemas.DoubleSchema",
|
|
1197
|
+
defaultNullable: true,
|
|
1198
|
+
defaultValue: "0.0"
|
|
1199
|
+
};
|
|
1200
|
+
const enumSchematicKind = {
|
|
1201
|
+
kind: "Enum",
|
|
1202
|
+
schema: "tech.ostack.kform.schemas.EnumSchema",
|
|
1203
|
+
defaultNullable: true,
|
|
1204
|
+
defaultValue: (schematic) => `${schematic.name}.${schematic.children?.[0].childName}`,
|
|
1205
|
+
builder: EnumBuilder,
|
|
1206
|
+
initChildren: () => [],
|
|
1207
|
+
defaultName: (childName) => pascalCase(childName),
|
|
1208
|
+
scaffoldModel: (schematic) => code`
|
|
1209
|
+
enum class ${schematic.name} {
|
|
1210
|
+
${schematic.children?.map((childSchematic) => childSchematic.childName).join(",\n")}
|
|
1211
|
+
}
|
|
1212
|
+
`
|
|
1213
|
+
};
|
|
1214
|
+
const fileSchematicKind = {
|
|
1215
|
+
kind: "File",
|
|
1216
|
+
package: "tech.ostack.kform.datatypes",
|
|
1217
|
+
name: "File",
|
|
1218
|
+
schema: "tech.ostack.kform.schemas.FileSchema",
|
|
1219
|
+
defaultNullable: true,
|
|
1220
|
+
defaultValue: (_schematic, data) => {
|
|
1221
|
+
data.currentFile.imports.add(
|
|
1222
|
+
"tech.ostack.kform.datatypes.emptyPlaceholderFile"
|
|
1223
|
+
);
|
|
1224
|
+
return "emptyPlaceholderFile()";
|
|
1225
|
+
},
|
|
1226
|
+
propertyAnnotations: (_schematic, data) => data.useFileBase64Serializer ? "@Serializable(with = File.Base64Serializer::class)" : void 0
|
|
1227
|
+
};
|
|
1228
|
+
const floatSchematicKind = {
|
|
1229
|
+
kind: "Float",
|
|
1230
|
+
package: "kotlin",
|
|
1231
|
+
name: "Float",
|
|
1232
|
+
schema: "tech.ostack.kform.schemas.FloatSchema",
|
|
1233
|
+
defaultNullable: true,
|
|
1234
|
+
defaultValue: "0f"
|
|
1235
|
+
};
|
|
1236
|
+
const instantSchematicKind = {
|
|
1237
|
+
kind: "Instant",
|
|
1238
|
+
package: "kotlin.time",
|
|
1239
|
+
name: "Instant",
|
|
1240
|
+
schema: "tech.ostack.kform.schemas.InstantSchema",
|
|
1241
|
+
defaultNullable: true,
|
|
1242
|
+
defaultValue: "Instant.fromEpochMilliseconds(0)",
|
|
1243
|
+
propertyAnnotations: (_schematic, data) => {
|
|
1244
|
+
data.currentFile.imports.add("kotlin.OptIn").add("kotlin.time.ExperimentalTime");
|
|
1245
|
+
return "@OptIn(ExperimentalTime::class)";
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
const intSchematicKind = {
|
|
1249
|
+
kind: "Int",
|
|
1250
|
+
package: "kotlin",
|
|
1251
|
+
name: "Int",
|
|
1252
|
+
schema: "tech.ostack.kform.schemas.IntSchema",
|
|
1253
|
+
defaultNullable: true,
|
|
1254
|
+
defaultValue: "0"
|
|
1255
|
+
};
|
|
1256
|
+
const listSchematicKind = {
|
|
1257
|
+
kind: "List",
|
|
1258
|
+
package: "kotlin.collections",
|
|
1259
|
+
name: "List",
|
|
1260
|
+
schema: "tech.ostack.kform.schemas.ListSchema",
|
|
1261
|
+
defaultValue: "emptyList()",
|
|
1262
|
+
builder: ListableBuilder,
|
|
1263
|
+
initChildren: () => [createSchematic()],
|
|
1264
|
+
scaffoldType: (schematic, data) => `List<${scaffoldType(schematic.children[0], {
|
|
1265
|
+
...data,
|
|
1266
|
+
currentPath: joinPaths(data.currentPath, "*")
|
|
1267
|
+
})}>`,
|
|
1268
|
+
scaffoldSchema: (schematic, data) => `ListSchema { ${scaffoldSchema(schematic.children[0], {
|
|
1269
|
+
...data,
|
|
1270
|
+
currentPath: joinPaths(data.currentPath, "*")
|
|
1271
|
+
})} }`
|
|
1272
|
+
};
|
|
1273
|
+
const localDateSchematicKind = {
|
|
1274
|
+
kind: "LocalDate",
|
|
1275
|
+
package: "kotlinx.datetime",
|
|
1276
|
+
name: "LocalDate",
|
|
1277
|
+
schema: "tech.ostack.kform.schemas.LocalDateSchema",
|
|
1278
|
+
defaultNullable: true,
|
|
1279
|
+
defaultValue: "LocalDate(1970, 1, 1)"
|
|
1280
|
+
};
|
|
1281
|
+
const localDateTimeSchematicKind = {
|
|
1282
|
+
kind: "LocalDateTime",
|
|
1283
|
+
package: "kotlinx.datetime",
|
|
1284
|
+
name: "LocalDateTime",
|
|
1285
|
+
schema: "tech.ostack.kform.schemas.LocalDateTimeSchema",
|
|
1286
|
+
defaultNullable: true,
|
|
1287
|
+
defaultValue: "LocalDateTime(1970, 1, 1, 0, 0)"
|
|
1288
|
+
};
|
|
1289
|
+
const longSchematicKind = {
|
|
1290
|
+
kind: "Long",
|
|
1291
|
+
package: "kotlin",
|
|
1292
|
+
name: "Long",
|
|
1293
|
+
schema: "tech.ostack.kform.schemas.LongSchema",
|
|
1294
|
+
defaultNullable: true,
|
|
1295
|
+
defaultValue: "0L"
|
|
1296
|
+
};
|
|
1297
|
+
const shortSchematicKind = {
|
|
1298
|
+
kind: "Short",
|
|
1299
|
+
package: "kotlin",
|
|
1300
|
+
name: "Short",
|
|
1301
|
+
schema: "tech.ostack.kform.schemas.ShortSchema",
|
|
1302
|
+
defaultNullable: true,
|
|
1303
|
+
defaultValue: "0.toShort()"
|
|
1304
|
+
};
|
|
1305
|
+
const stringSchematicKind = {
|
|
1306
|
+
kind: "String",
|
|
1307
|
+
package: "kotlin",
|
|
1308
|
+
name: "String",
|
|
1309
|
+
schema: "tech.ostack.kform.schemas.StringSchema",
|
|
1310
|
+
defaultValue: '""'
|
|
1311
|
+
};
|
|
1312
|
+
const tableSchematicKind = {
|
|
1313
|
+
kind: "Table",
|
|
1314
|
+
package: "tech.ostack.kform.datatypes",
|
|
1315
|
+
name: "Table",
|
|
1316
|
+
schema: "tech.ostack.kform.schemas.TableSchema",
|
|
1317
|
+
defaultValue: (_schematic, data) => {
|
|
1318
|
+
data.currentFile.imports.add("tech.ostack.kform.datatypes.tableOf");
|
|
1319
|
+
return "tableOf()";
|
|
1320
|
+
},
|
|
1321
|
+
builder: ListableBuilder,
|
|
1322
|
+
initChildren: () => [createSchematic()],
|
|
1323
|
+
propertyAnnotations: (_schematic, data) => data.useTableValuesSerializer ? "@Serializable(with = Table.ValuesSerializer::class)" : void 0,
|
|
1324
|
+
scaffoldType: (schematic, data) => `Table<${scaffoldType(schematic.children[0], {
|
|
1325
|
+
...data,
|
|
1326
|
+
currentPath: joinPaths(data.currentPath, "*")
|
|
1327
|
+
})}>`,
|
|
1328
|
+
scaffoldSchema: (schematic, data) => `TableSchema { ${scaffoldSchema(schematic.children[0], {
|
|
1329
|
+
...data,
|
|
1330
|
+
currentPath: joinPaths(data.currentPath, "*")
|
|
1331
|
+
})} }`
|
|
1332
|
+
};
|
|
1333
|
+
const defaultSchematicKinds = [
|
|
1334
|
+
anySchematicKind,
|
|
1335
|
+
bigDecimalSchematicKind,
|
|
1336
|
+
bigIntegerSchematicKind,
|
|
1337
|
+
booleanSchematicKind,
|
|
1338
|
+
byteSchematicKind,
|
|
1339
|
+
charSchematicKind,
|
|
1340
|
+
classSchematicKind,
|
|
1341
|
+
doubleSchematicKind,
|
|
1342
|
+
enumSchematicKind,
|
|
1343
|
+
fileSchematicKind,
|
|
1344
|
+
floatSchematicKind,
|
|
1345
|
+
instantSchematicKind,
|
|
1346
|
+
intSchematicKind,
|
|
1347
|
+
listSchematicKind,
|
|
1348
|
+
localDateSchematicKind,
|
|
1349
|
+
localDateTimeSchematicKind,
|
|
1350
|
+
longSchematicKind,
|
|
1351
|
+
shortSchematicKind,
|
|
1352
|
+
stringSchematicKind,
|
|
1353
|
+
tableSchematicKind
|
|
1354
|
+
];
|
|
1355
|
+
function scaffoldSchemas(schematic, data) {
|
|
1356
|
+
const fileName = joinPaths(data.currentDir, `${schematic.name}Schema.kt`);
|
|
1357
|
+
let file = data.files.get(fileName);
|
|
1358
|
+
if (!file) {
|
|
1359
|
+
data.files.set(fileName, file = ktFile(data));
|
|
1360
|
+
data.files.set(
|
|
1361
|
+
joinPaths(data.currentDir, `${schematic.name}Validations.kt`),
|
|
1362
|
+
ktFile(data)
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
scaffoldSchemaDeclaration(schematic, { ...data, currentFile: file });
|
|
1321
1366
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1367
|
+
function scaffoldSchemaDeclaration(schematic, data) {
|
|
1368
|
+
const schematicKind = data.schematicKinds.get(schematic.kind);
|
|
1369
|
+
if (!schematicKind) {
|
|
1370
|
+
throw new Error(`Unknown schematic kind ${schematic.kind}`);
|
|
1371
|
+
}
|
|
1372
|
+
const nDecls = data.currentFile.declarations.length;
|
|
1373
|
+
data.currentFile.declarations.push("");
|
|
1374
|
+
const decl = `val ${schematic.name}Schema = ${schematicKind.scaffoldSchema?.(schematic, data) ?? `${simpleKtName(schematicKind.schema)}()`}`;
|
|
1375
|
+
data.currentFile.imports.add(schematicKind.schema);
|
|
1376
|
+
data.currentFile.declarations[nDecls] = data.currentPath === "/" ? jsExport(decl, data) : decl;
|
|
1329
1377
|
}
|
|
1330
|
-
function
|
|
1331
|
-
|
|
1378
|
+
function scaffoldSchema(schematic, data) {
|
|
1379
|
+
const schematicKind = data.schematicKinds.get(schematic.kind);
|
|
1380
|
+
if (!schematicKind) {
|
|
1381
|
+
throw new Error(`Unknown schematic kind ${schematic.kind}`);
|
|
1382
|
+
}
|
|
1383
|
+
const schematicPackage = schematicKind.package ?? joinPackages(data.currentPackage, schematic.packageSuffix);
|
|
1384
|
+
let scaffoldedSchema;
|
|
1385
|
+
if (schematicKind.package == null && schematicPackage !== data.currentPackage) {
|
|
1386
|
+
scaffoldedSchema = `${schematic.name}Schema`;
|
|
1387
|
+
data.currentFile.imports.add(`${schematicPackage}.${scaffoldedSchema}`);
|
|
1388
|
+
scaffoldSchemas(schematic, {
|
|
1389
|
+
...data,
|
|
1390
|
+
currentPackage: joinPackages(
|
|
1391
|
+
data.currentPackage,
|
|
1392
|
+
schematic.packageSuffix
|
|
1393
|
+
),
|
|
1394
|
+
currentDir: joinPaths(
|
|
1395
|
+
data.currentDir,
|
|
1396
|
+
schematic.packageSuffix?.replace(".", "/")
|
|
1397
|
+
)
|
|
1398
|
+
});
|
|
1399
|
+
} else {
|
|
1400
|
+
scaffoldedSchema = schematicKind.scaffoldSchema?.(schematic, data) ?? `${simpleKtName(schematicKind.schema)}()`;
|
|
1401
|
+
data.currentFile.imports.add(schematicKind.schema);
|
|
1402
|
+
}
|
|
1403
|
+
if (schematic.nullable) {
|
|
1404
|
+
data.currentFile.imports.add(NULLABLE_SCHEMA);
|
|
1405
|
+
return `${simpleKtName(NULLABLE_SCHEMA)} { ${scaffoldedSchema} }`;
|
|
1406
|
+
}
|
|
1407
|
+
return scaffoldedSchema;
|
|
1332
1408
|
}
|
|
1409
|
+
const serializerKt = "package <%= filePackage %>\n\nimport kotlin.js.JsExport\nimport kotlin.jvm.JvmOverloads\nimport kotlinx.serialization.json.Json\n\n/** Base JSON configuration. */\nprivate val JSON_CONFIG = Json.Default\n/** Pretty JSON configuration. */\nprivate val JSON_CONFIG_PRETTY = Json(JSON_CONFIG) { prettyPrint = true }\n\n/** JSON configuration. */\n@JvmOverloads\nfun jsonConfig(prettyPrint: Boolean = false): Json =\n if (prettyPrint) JSON_CONFIG_PRETTY else JSON_CONFIG\n\n@JsExport\n@JvmOverloads\nfun encode<%= formClass %>ToString(<%= formVar %>: <%= formClass %>, prettyPrint: Boolean = false): String =\n jsonConfig(prettyPrint).encodeToString(<%= formVar %>)\n\n@JsExport\nfun decode<%= formClass %>FromString(json: String): <%= formClass %> =\n jsonConfig().decodeFromString(json)\n";
|
|
1333
1410
|
function ejsTemplateFile(content, ejsData, { base64, binary, executable, ...ejsOptions } = {}) {
|
|
1334
1411
|
return {
|
|
1335
1412
|
content,
|
|
@@ -1549,6 +1626,16 @@ function SchematicBuilder({
|
|
|
1549
1626
|
}
|
|
1550
1627
|
);
|
|
1551
1628
|
}
|
|
1629
|
+
function templateFile(content, options) {
|
|
1630
|
+
return {
|
|
1631
|
+
...options,
|
|
1632
|
+
content,
|
|
1633
|
+
getContent: templateFileContent
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
function templateFileContent() {
|
|
1637
|
+
return this.content;
|
|
1638
|
+
}
|
|
1552
1639
|
function addTemplateFile(data, fileName, content, options) {
|
|
1553
1640
|
data.files.set(
|
|
1554
1641
|
joinPaths(data.currentDir, fileName),
|
|
@@ -1594,15 +1681,22 @@ function tsImportsToStatements(imports) {
|
|
|
1594
1681
|
return Array.from(importsStatements.values());
|
|
1595
1682
|
}
|
|
1596
1683
|
function tsImportStatementToString(importStatement) {
|
|
1597
|
-
const namedImportsArr =
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1684
|
+
const namedImportsArr = [
|
|
1685
|
+
.../* @__PURE__ */ new Set([
|
|
1686
|
+
...importStatement.namedImports ?? [],
|
|
1687
|
+
...importStatement.typedImports ?? []
|
|
1688
|
+
])
|
|
1689
|
+
].sort((i1, i2) => i1.localeCompare(i2));
|
|
1690
|
+
const allNamedImportsAreTyped = namedImportsArr.every(
|
|
1691
|
+
(i) => !importStatement.namedImports?.has(i)
|
|
1692
|
+
);
|
|
1693
|
+
const namedImports = namedImportsArr.length > 0 ? allNamedImportsAreTyped ? `type { ${namedImportsArr.join(", ")} }` : `{ ${namedImportsArr.map(
|
|
1694
|
+
(i) => importStatement.namedImports?.has(i) ? i : `type ${i}`
|
|
1695
|
+
).join(", ")} }` : null;
|
|
1602
1696
|
const defaultImport = importStatement.defaultImport || null;
|
|
1603
1697
|
const namespaceImport = importStatement.namespaceImport ? `* as ${importStatement.namespaceImport}` : null;
|
|
1604
|
-
const allImports = [defaultImport, namespaceImport,
|
|
1605
|
-
const moduleName =
|
|
1698
|
+
const allImports = [defaultImport, namespaceImport, namedImports].filter((s) => s).join(", ");
|
|
1699
|
+
const moduleName = JSON.stringify(importStatement.moduleName);
|
|
1606
1700
|
return allImports ? `import ${allImports} from ${moduleName};` : `import ${moduleName};`;
|
|
1607
1701
|
}
|
|
1608
1702
|
export {
|