@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 { useRef, createContext, useContext, useCallback, useMemo, useLayoutEffect as useLayoutEffect$1, useState } from "react";
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
- function scaffoldSchemas(schematic, data) {
189
- const fileName = joinPaths(data.currentDir, `${schematic.name}Schema.kt`);
190
- let file = data.files.get(fileName);
191
- if (!file) {
192
- data.files.set(fileName, file = ktFile(data));
193
- data.files.set(
194
- joinPaths(data.currentDir, `${schematic.name}Validations.kt`),
195
- ktFile(data)
196
- );
197
- }
198
- scaffoldSchemaDeclaration(schematic, { ...data, currentFile: file });
199
- }
200
- function scaffoldSchemaDeclaration(schematic, data) {
201
- const schematicKind = data.schematicKinds.get(schematic.kind);
202
- if (!schematicKind) {
203
- throw new Error(`Unknown schematic kind ${schematic.kind}`);
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
- const nDecls = data.currentFile.declarations.length;
206
- data.currentFile.declarations.push("");
207
- const decl = `val ${schematic.name}Schema = ${schematicKind.scaffoldSchema?.(schematic, data) ?? `${simpleKtName(schematicKind.schema)}()`}`;
208
- data.currentFile.imports.add(schematicKind.schema);
209
- data.currentFile.declarations[nDecls] = data.currentPath === "/" ? jsExport(decl, data) : decl;
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 scaffoldSchema(schematic, data) {
212
- const schematicKind = data.schematicKinds.get(schematic.kind);
213
- if (!schematicKind) {
214
- throw new Error(`Unknown schematic kind ${schematic.kind}`);
215
- }
216
- const schematicPackage = schematicKind.package ?? joinPackages(data.currentPackage, schematic.packageSuffix);
217
- let scaffoldedSchema;
218
- if (schematicKind.package == null && schematicPackage !== data.currentPackage) {
219
- scaffoldedSchema = `${schematic.name}Schema`;
220
- data.currentFile.imports.add(`${schematicPackage}.${scaffoldedSchema}`);
221
- scaffoldSchemas(schematic, {
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
- if (schematic.nullable) {
237
- data.currentFile.imports.add(NULLABLE_SCHEMA);
238
- return `${simpleKtName(NULLABLE_SCHEMA)} { ${scaffoldedSchema} }`;
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 scaffoldedSchema;
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
- type: "checkbox",
1313
- checked: config,
1314
- onChange: (evt) => setConfig(evt.target.checked),
1315
- disabled
1181
+ ...data,
1182
+ currentPath: joinPaths(
1183
+ data.currentPath,
1184
+ childSchematic.childName
1185
+ )
1316
1186
  }
1317
- ),
1318
- "Use ",
1319
- /* @__PURE__ */ jsx("code", { children: "Table.ValuesSerializer" })
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
- 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";
1323
- function templateFile(content, options) {
1324
- return {
1325
- ...options,
1326
- content,
1327
- getContent: templateFileContent
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 templateFileContent() {
1331
- return this.content;
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 = importStatement.namedImports ? Array.from(importStatement.namedImports).sort(
1598
- (i1, i2) => i1.localeCompare(i2)
1599
- ) : [];
1600
- const typedImportsArr = importStatement.typedImports ? Array.from(importStatement.typedImports).sort((i1, i2) => i1.localeCompare(i2)).map((i) => `type ${i}`) : [];
1601
- const namedTypedImports = namedImportsArr.length > 0 || typedImportsArr.length > 0 ? `{ ${[...namedImportsArr, ...typedImportsArr].join(", ")} }` : null;
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, namedTypedImports].filter((s) => s).join(", ");
1605
- const moduleName = `"${importStatement.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 {