@kform/scaffolder
Version:
Scaffolding utilities for KForm projects.
1 lines • 112 kB
Source Map (JSON)
{"version":3,"file":"kform-scaffolder.cjs","sources":["../../src/Schematic.ts","../../src/SchematicBuilderContext.ts","../../src/actions/LoadSchematic.tsx","../../src/actions/SaveSchematic.tsx","../../src/scaffolding/configScaffolder.ts","../../src/util/join.ts","../../src/scaffolding/scaffold.ts","../../src/scaffolding/useScaffolder.ts","../../src/actions/ScaffoldToZip.tsx","../../src/configs/util/useConfig.ts","../../src/configs/UseFileBase64SerializerConfig.tsx","../../src/configs/UseTableValuesSerializerConfig.tsx","../../src/scaffolding/kotlin/util/defaultImports.ts","../../src/scaffolding/kotlin/KtFile.ts","../../src/scaffolding/kotlin/util/simpleKtName.ts","../../src/scaffolding/kotlin/util/annotations.ts","../../src/scaffolding/scaffolders/scaffoldModels.ts","../../src/util/boolDataAttr.ts","../../src/util/preventDrag.ts","../../src/schematicBuilders/util/ChildMarker.tsx","../../src/schematicBuilders/util/ChildRemove.tsx","../../src/schematicBuilders/util/KindSelect.tsx","../../src/schematicBuilders/util/NullableInput.tsx","../../src/schematicBuilders/util/PackageNameInput.tsx","../../src/schematicBuilders/ClassBuilder.tsx","../../src/schematicBuilders/EnumBuilder.tsx","../../src/schematicBuilders/ListableBuilder.tsx","../../src/util/code.ts","../../src/SchematicKind.ts","../../src/scaffolding/scaffolders/scaffoldSchemas.ts","../../src/scaffolding/templates/Serializer.kt.ejs?raw","../../src/scaffolding/templating/EjsTemplateFile.ts","../../src/scaffolding/templating/util/addEjsTemplateFile.ts","../../src/scaffolding/scaffolders/scaffoldSerializer.ts","../../src/scaffolding/templates/Validator.kt.ejs?raw","../../src/scaffolding/scaffolders/scaffoldValidator.ts","../../src/util/useLayoutEffect.ts","../../src/util/useMeasure.ts","../../src/SchematicBuilderConfig.tsx","../../src/SchematicBuilder.tsx","../../src/scaffolding/templating/TemplateFile.ts","../../src/scaffolding/templating/util/addTemplateFile.ts","../../src/scaffolding/typescript/TsFile.ts"],"sourcesContent":["export interface Schematic {\n id: string;\n kind: string;\n packageSuffix?: string;\n name?: string;\n nullable?: boolean;\n collapsed?: boolean;\n children?: Schematic[];\n childName?: string;\n config?: Record<string, unknown>;\n}\n\nexport function createSchematic(schematic?: Partial<Schematic>): Schematic {\n return {\n id: self.crypto.randomUUID(),\n kind: \"\",\n ...schematic,\n };\n}\n\nexport function moveSchematic(\n schematic: Schematic,\n moveId: string,\n parentId: string,\n sortBefore: string | null,\n): Schematic {\n const { schematic: schematicWithoutFrom, removed: fromSchematic } = remove(\n schematic,\n moveId,\n );\n return add(schematicWithoutFrom!, fromSchematic!, parentId, sortBefore);\n}\n\nfunction remove(\n schematic: Schematic,\n id: string,\n): { schematic: Schematic | null; removed: Schematic | null } {\n if (schematic.id === id) {\n return { schematic: null, removed: schematic };\n }\n if (!schematic.children) {\n return { schematic, removed: null };\n }\n const newChildren = [];\n let removed: Schematic | null = null;\n for (const child of schematic.children) {\n const { schematic: newChild, removed: removedChild } = remove(child, id);\n if (newChild !== null) {\n newChildren.push(newChild);\n }\n if (removedChild !== null) {\n removed = removedChild;\n }\n }\n return { schematic: { ...schematic, children: newChildren }, removed };\n}\n\nfunction add(\n schematic: Schematic,\n toAdd: Schematic,\n parentId: string,\n sortBefore: string | null,\n): Schematic {\n if (schematic.id === parentId) {\n const oldChildren = schematic.children ?? [];\n const index =\n sortBefore === null\n ? oldChildren.length\n : oldChildren.findIndex((c) => c.id === sortBefore);\n return {\n ...schematic,\n children: [\n ...oldChildren.slice(0, index),\n toAdd,\n ...oldChildren.slice(index),\n ],\n };\n }\n if (!schematic.children) {\n return schematic;\n }\n return {\n ...schematic,\n children: schematic.children.map((child) =>\n add(child, toAdd, parentId, sortBefore),\n ),\n };\n}\n","import * as React from \"react\";\n\nimport { Scaffolder } from \"./scaffolding/scaffold\";\nimport { Schematic } from \"./Schematic\";\nimport { SchematicKind } from \"./SchematicKind\";\n\nexport interface DraggedSchematic {\n id: string;\n type: string;\n node: React.ReactNode;\n}\n\nexport interface SchematicBuilderContextValue {\n name: string;\n basePath?: string;\n basePackage?: string;\n baseDir?: string | ((schematic: Schematic) => string);\n schematicKinds: Map<string, SchematicKind>;\n scaffolders:\n | Scaffolder<any>[]\n | ((schematic: Schematic) => Scaffolder<any>[]);\n scaffoldingData?:\n | Record<string, any>\n | ((schematic: Schematic) => Record<string, any>);\n getRootSchematic: () => Schematic;\n useSchematic: <T>(selector: (schematic: Schematic) => T) => T;\n setSchematic: ((schematic: Partial<Schematic>) => void) &\n ((setter: (schematic: Schematic) => Partial<Schematic>) => void);\n removeSchematic: () => void;\n parentPackage?: string;\n parentId?: string;\n parentChildName?: string;\n draggedSchematic: DraggedSchematic | null;\n disableDrop?: boolean;\n ignoreDrop?: boolean;\n latestSchematicFileHandle: React.MutableRefObject<FileSystemFileHandle | null>;\n}\n\nexport const SchematicBuilderContext =\n React.createContext<SchematicBuilderContextValue | null>(null);\n\nexport function useSchematicBuilderContext(): SchematicBuilderContextValue {\n const context = React.useContext(SchematicBuilderContext);\n if (context === null) {\n throw new Error(\"Schematic builder not in context.\");\n }\n return context;\n}\n","import { fileOpen } from \"browser-fs-access\";\n\nimport { useSchematicBuilderContext } from \"../SchematicBuilderContext\";\n\nexport function LoadSchematic() {\n const { name, setSchematic, latestSchematicFileHandle } =\n useSchematicBuilderContext();\n\n return (\n <button\n className=\"builder-icon-button builder-action builder-load-schematic\"\n type=\"button\"\n onClick={async () => {\n try {\n const file = await fileOpen({\n extensions: [\".json\"],\n mimeTypes: [\"application/json\"],\n description: \"Form schematic\",\n id: name,\n });\n if (file.handle) {\n latestSchematicFileHandle.current = file.handle;\n }\n const content = JSON.parse(await file.text());\n setSchematic(() => content);\n } catch (err) {\n // User aborted\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return;\n }\n\n alert(`Unable to load schematic: ${err?.toString()}`);\n }\n }}\n title=\"Load schematic\"\n aria-label=\"Load schematic\"\n >\n 📂\n </button>\n );\n}\n","import { fileSave, supported } from \"browser-fs-access\";\n\nimport { Schematic } from \"../Schematic\";\nimport { useSchematicBuilderContext } from \"../SchematicBuilderContext\";\n\nexport function SaveSchematic() {\n const { name, getRootSchematic, latestSchematicFileHandle } =\n useSchematicBuilderContext();\n\n return (\n <button\n className=\"builder-icon-button builder-action builder-save-schematic\"\n type=\"button\"\n onClick={async () => {\n try {\n const schematic: Schematic = getRootSchematic();\n latestSchematicFileHandle.current = await fileSave(\n new Blob([JSON.stringify(schematic, null, 2)]),\n {\n fileName: `${schematic.name}-schematic.json`,\n extensions: [\".json\"],\n mimeTypes: [\"application/json\"],\n description: \"Form schematic\",\n id: name,\n },\n latestSchematicFileHandle.current,\n );\n if (supported) {\n alert(\n `Schematic saved successfully as “${latestSchematicFileHandle.current!.name}”.`,\n );\n }\n } catch (err) {\n // User aborted\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return;\n }\n\n alert(`Unable to save schematic: ${err?.toString()}`);\n }\n }}\n title=\"Save schematic\"\n aria-label=\"Save schematic\"\n >\n 💾\n </button>\n );\n}\n","import { Schematic } from \"../Schematic\";\nimport { Scaffolder } from \"./scaffold\";\nimport { ScaffoldingData } from \"./ScaffoldingData\";\n\n/** Returns a new scaffolder with its data merged with the provided data. */\nexport function configScaffolder<TScaffoldingData extends ScaffoldingData>(\n scaffolder: Scaffolder<TScaffoldingData>,\n data: Partial<TScaffoldingData>,\n): Scaffolder<TScaffoldingData> {\n return (schematic: Schematic, originalData: TScaffoldingData) =>\n scaffolder(schematic, { ...originalData, ...data });\n}\n","function join(\n s1: string | undefined,\n s2: string | undefined,\n separator: string,\n): string {\n return !s1 || !s2\n ? s1 || s2 || \"\"\n : `${s1}${s1.endsWith(separator) ? \"\" : separator}${s2}`;\n}\n\nexport function joinPaths(\n path1: string | undefined,\n path2: string | undefined,\n): string {\n return join(path1, path2, \"/\");\n}\n\nexport function joinPackages(\n pkg1: string | undefined,\n pkg2: string | undefined,\n): string {\n return join(pkg1, pkg2, \".\");\n}\n","import { Schematic } from \"../Schematic\";\nimport { SchematicKind } from \"../SchematicKind\";\nimport { joinPackages } from \"../util/join\";\nimport { ScaffoldingData } from \"./ScaffoldingData\";\nimport { ScaffoldingFile, ScaffoldingFileContent } from \"./ScaffoldingFile\";\n\nexport interface ScaffoldOptions {\n schematicKinds: Map<string, SchematicKind>;\n basePath?: string;\n basePackage?: string;\n baseDir?: string;\n}\n\nexport type Scaffolder<\n TScaffoldingData extends ScaffoldingData = ScaffoldingData,\n> = (schematic: Schematic, data: TScaffoldingData) => void;\n\nexport interface ScaffoldedFile extends Omit<ScaffoldingFile, \"getContent\"> {\n path: string;\n content: ScaffoldingFileContent | Promise<ScaffoldingFileContent>;\n}\n\nexport function scaffold(\n schematic: Schematic,\n scaffolders: Scaffolder<any>[],\n options: ScaffoldOptions,\n): ScaffoldedFile[] {\n const files = new Map<string, ScaffoldingFile>();\n const data: ScaffoldingData = {\n schematicKinds: options.schematicKinds,\n rootSchematic: schematic,\n files,\n currentPath: options.basePath ?? \"/\",\n currentPackage: joinPackages(options.basePackage, schematic.packageSuffix),\n currentDir: options.baseDir,\n };\n for (const scaffolder of scaffolders) {\n scaffolder(schematic, data);\n }\n return Array.from(files).map(([path, file]) => ({\n path,\n content: file.getContent(),\n base64: file.base64,\n binary: file.binary,\n executable: file.executable,\n }));\n}\n","import * as React from \"react\";\n\nimport { useSchematicBuilderContext } from \"../SchematicBuilderContext\";\nimport { configScaffolder } from \"./configScaffolder\";\nimport { scaffold, ScaffoldedFile } from \"./scaffold\";\n\nexport function useScaffolder(): () => ScaffoldedFile[] {\n const {\n basePath,\n basePackage,\n baseDir,\n schematicKinds,\n scaffolders,\n scaffoldingData,\n getRootSchematic,\n } = useSchematicBuilderContext();\n return React.useCallback(() => {\n const schematic = getRootSchematic();\n\n let actualScaffolders =\n typeof scaffolders === \"function\" ? scaffolders(schematic) : scaffolders;\n const actualScaffoldingData =\n typeof scaffoldingData === \"function\"\n ? scaffoldingData(schematic)\n : scaffoldingData;\n\n if (actualScaffoldingData) {\n actualScaffolders = actualScaffolders.map((scaffolder) =>\n configScaffolder(scaffolder, actualScaffoldingData),\n );\n }\n\n return scaffold(schematic, actualScaffolders, {\n schematicKinds,\n basePath,\n basePackage,\n baseDir: typeof baseDir === \"function\" ? baseDir(schematic) : baseDir,\n });\n }, [\n baseDir,\n basePackage,\n basePath,\n getRootSchematic,\n scaffolders,\n scaffoldingData,\n schematicKinds,\n ]);\n}\n","import { fileSave, supported } from \"browser-fs-access\";\nimport JSZip from \"jszip\";\n\nimport { useScaffolder } from \"../scaffolding/useScaffolder\";\nimport { useSchematicBuilderContext } from \"../SchematicBuilderContext\";\n\nlet latestZipFileHandle: FileSystemFileHandle | null = null;\n\nexport function ScaffoldToZip() {\n const { getRootSchematic, name } = useSchematicBuilderContext();\n const scaffold = useScaffolder();\n\n return (\n <button\n className=\"builder-input builder-action builder-scaffold-zip\"\n type=\"submit\"\n onClick={async (evt) => {\n evt.preventDefault();\n try {\n const schematic = getRootSchematic();\n const files = scaffold();\n const zip = new JSZip();\n for (const { path, content, base64, binary, executable } of files) {\n zip.file(path, content, {\n base64,\n binary,\n unixPermissions: executable ? \"755\" : undefined,\n });\n }\n latestZipFileHandle = await fileSave(\n zip.generateAsync({ platform: \"UNIX\", type: \"blob\" }),\n {\n fileName: `${schematic.name}.zip`,\n extensions: [\".zip\"],\n mimeTypes: [\"application/zip\"],\n description: \"Form scaffolding\",\n id: name,\n },\n latestZipFileHandle,\n );\n if (supported) {\n alert(\n `Project scaffolded successfully in “${latestZipFileHandle!.name}”.`,\n );\n }\n } catch (err) {\n // User aborted\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return;\n }\n\n alert(`Unable to scaffold project: ${err?.toString()}`);\n }\n }}\n >\n Scaffold 🏗️\n </button>\n );\n}\n","import * as React from \"react\";\n\nimport { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\n\ntype UseConfigReturnType<T = unknown> = [\n T,\n (configValue: T | ((configValue: T) => T)) => void,\n];\n\nexport function useConfig<T = unknown>(\n configName: string,\n): UseConfigReturnType<T | undefined>;\nexport function useConfig<T = unknown>(\n configName: string,\n defaultValue: T,\n): UseConfigReturnType<T>;\nexport function useConfig<T>(\n configName: string,\n defaultValue?: T,\n): UseConfigReturnType<T> {\n const { useSchematic, setSchematic } = useSchematicBuilderContext();\n const configValue = useSchematic(\n (schematic) => (schematic.config?.[configName] ?? defaultValue) as T,\n );\n const setConfigValue = React.useCallback(\n (configValue: T | ((configValue: T) => T)) =>\n setSchematic((schematic) => ({\n config: {\n ...schematic.config,\n [configName]:\n typeof configValue === \"function\"\n ? (configValue as any)(schematic.config?.[configName] as T)\n : configValue,\n },\n })),\n [configName, setSchematic],\n );\n\n return React.useMemo(\n () => [configValue, setConfigValue],\n [configValue, setConfigValue],\n );\n}\n","import { useConfig } from \"./util/useConfig\";\n\nexport interface UseFileBase64SerializerConfigProps {\n disabled?: boolean;\n}\n\nexport function UseFileBase64SerializerConfig({\n disabled,\n}: UseFileBase64SerializerConfigProps) {\n const [config, setConfig] = useConfig(\"useFileBase64Serializer\", false);\n return (\n <div className=\"builder-config-field\">\n <label>\n <input\n type=\"checkbox\"\n checked={config}\n onChange={(evt) => setConfig(evt.target.checked)}\n disabled={disabled}\n />\n Use <code>File.Base64Serializer</code>\n </label>\n </div>\n );\n}\n","import { useConfig } from \"./util/useConfig\";\n\nexport interface UseTableValuesSerializerConfigProps {\n disabled?: boolean;\n}\n\nexport function UseTableValuesSerializerConfig({\n disabled,\n}: UseTableValuesSerializerConfigProps) {\n const [config, setConfig] = useConfig(\"useTableValuesSerializer\", false);\n return (\n <div className=\"builder-config-field\">\n <label>\n <input\n type=\"checkbox\"\n checked={config}\n onChange={(evt) => setConfig(evt.target.checked)}\n disabled={disabled}\n />\n Use <code>Table.ValuesSerializer</code>\n </label>\n </div>\n );\n}\n","const DEFAULT_IMPORTS = [\n /^kotlin\\.[^.]+$/,\n /^kotlin\\.annotation\\.[^.]+$/,\n /^kotlin\\.collections\\.[^.]+$/,\n /^kotlin\\.comparisons\\.[^.]+$/,\n /^kotlin\\.io\\.[^.]+$/,\n /^kotlin\\.ranges\\.[^.]+$/,\n /^kotlin\\.sequences\\.[^.]+$/,\n /^kotlin\\.text\\.[^.]+$/,\n];\n\nexport function isDefaultKtImport(qualifiedName: string): boolean {\n return DEFAULT_IMPORTS.some((i) => i.test(qualifiedName));\n}\n","import { ScaffoldingData } from \"../ScaffoldingData\";\nimport { ScaffoldingFile } from \"../ScaffoldingFile\";\nimport { isDefaultKtImport } from \"./util/defaultImports\";\n\nexport interface KtFile extends ScaffoldingFile {\n package: string;\n imports: Set<string>;\n declarations: string[];\n}\n\nexport function ktFile(data: ScaffoldingData): KtFile {\n return {\n package: data.currentPackage ?? \"\",\n imports: new Set<string>(),\n declarations: [],\n getContent: ktFileContent,\n };\n}\n\nfunction ktFileContent(this: KtFile): string {\n return (\n [\n // Package\n this.package && `package ${this.package}`,\n\n // Imports\n Array.from(this.imports)\n .filter((i) => !isDefaultKtImport(i))\n .sort()\n .map((i) => `import ${i}`)\n .join(\"\\n\"),\n\n // Declarations\n ...this.declarations,\n ]\n .filter((s) => s)\n .join(\"\\n\\n\") + \"\\n\"\n );\n}\n","export function simpleKtName(qualifiedName: string): string {\n return qualifiedName.substring(qualifiedName.lastIndexOf(\".\") + 1);\n}\n","import { KtData } from \"../KtData\";\nimport { simpleKtName } from \"./simpleKtName\";\n\nconst JS_EXPORT = \"kotlin.js.JsExport\";\nconst SERIALIZABLE = \"kotlinx.serialization.Serializable\";\n\nexport function annotate(\n annotations: string | string[] | undefined,\n code: string,\n): string {\n if (!annotations || annotations.length === 0) {\n return code;\n }\n return [\n ...(typeof annotations === \"string\" ? [annotations] : annotations),\n code,\n ].join(\"\\n\");\n}\n\nexport function jsExport(code: string, data: KtData): string {\n data.currentFile.imports.add(JS_EXPORT);\n return annotate(`@${simpleKtName(JS_EXPORT)}`, code);\n}\n\nexport function serializable(code: string, data: KtData): string {\n data.currentFile.imports.add(SERIALIZABLE);\n return annotate(`@${simpleKtName(SERIALIZABLE)}`, code);\n}\n","import { Schematic } from \"../../Schematic\";\nimport { joinPackages, joinPaths } from \"../../util/join\";\nimport { KtData } from \"../kotlin/KtData\";\nimport { KtFile, ktFile } from \"../kotlin/KtFile\";\nimport { jsExport, serializable } from \"../kotlin/util/annotations\";\nimport { ScaffoldingData } from \"../ScaffoldingData\";\n\nexport function scaffoldModels(\n schematic: Schematic,\n data: ScaffoldingData,\n): void {\n const fileName = joinPaths(data.currentDir, `${schematic.name}.kt`);\n let file = data.files.get(fileName) as KtFile | undefined;\n if (!file) {\n data.files.set(fileName, (file = ktFile(data)));\n }\n scaffoldModelDeclaration(schematic, { ...data, currentFile: file });\n}\n\nfunction scaffoldModelDeclaration(schematic: Schematic, data: KtData): void {\n const schematicKind = data.schematicKinds.get(schematic.kind);\n if (!schematicKind) {\n throw new Error(`Unknown schematic kind ${schematic.kind}`);\n }\n\n const nDecls = data.currentFile.declarations.length;\n data.currentFile.declarations.push(\"\");\n const decl =\n schematicKind.scaffoldModel?.(schematic, data) ??\n `typealias ${schematic.name} = ${scaffoldType(schematic, data)}`;\n data.currentFile.declarations[nDecls] = schematicKind.scaffoldModel\n ? jsExport(serializable(decl, data), data)\n : decl;\n}\n\nexport function scaffoldPropertyAnnotations(\n schematic: Schematic,\n data: KtData,\n): string | string[] | undefined {\n const schematicKind = data.schematicKinds.get(schematic.kind);\n if (!schematicKind) {\n throw new Error(`Unknown schematic kind ${schematic.kind}`);\n }\n\n return typeof schematicKind.propertyAnnotations === \"function\"\n ? schematicKind.propertyAnnotations(schematic, data)\n : schematicKind.propertyAnnotations;\n}\n\nexport function scaffoldType(schematic: Schematic, data: KtData): string {\n const schematicKind = data.schematicKinds.get(schematic.kind);\n if (!schematicKind) {\n throw new Error(`Unknown schematic kind ${schematic.kind}`);\n }\n\n const schematicPackage =\n schematicKind.package ??\n joinPackages(data.currentPackage, schematic.packageSuffix);\n const schematicName = schematic.name || schematicKind.name;\n\n if (schematicPackage !== data.currentPackage) {\n data.currentFile.imports.add(joinPackages(schematicPackage, schematicName));\n }\n\n if (\n schematicKind.package == null &&\n schematicPackage !== data.currentPackage\n ) {\n scaffoldModels(schematic, {\n ...data,\n currentPackage: joinPackages(\n data.currentPackage,\n schematic.packageSuffix,\n ),\n currentDir: joinPaths(\n data.currentDir,\n schematic.packageSuffix?.replace(\".\", \"/\"),\n ),\n });\n } else if (schematicKind.scaffoldModel) {\n scaffoldModelDeclaration(schematic, data);\n }\n\n return (\n (schematicKind.scaffoldType?.(schematic, data) ?? schematicName) +\n (schematic.nullable ? \"?\" : \"\")\n );\n}\n\nexport function scaffoldDefaultValue(\n schematic: Schematic,\n data: KtData,\n): string {\n const schematicKind = data.schematicKinds.get(schematic.kind);\n if (!schematicKind) {\n throw new Error(`Unknown schematic kind ${schematic.kind}`);\n }\n\n return schematic.nullable\n ? \"null\"\n : typeof schematicKind.defaultValue === \"function\"\n ? schematicKind.defaultValue(schematic, data)\n : schematicKind.defaultValue;\n}\n","/** Value for a data attribute given a condition. */\nexport function boolDataAttr(\n condition: boolean | null | undefined,\n): string | undefined {\n return condition ? \"\" : undefined;\n}\n","import * as React from \"react\";\n\n/** Properties used to prevent an element from being dragged. */\nexport const preventDrag = {\n onKeyDown: (evt: React.KeyboardEvent) => evt.stopPropagation(),\n onPointerDown: (evt: React.PointerEvent) => evt.stopPropagation(),\n};\n","import { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\nimport { preventDrag } from \"../../util/preventDrag\";\n\nexport function ChildMarker() {\n const { schematicKinds, useSchematic, setSchematic } =\n useSchematicBuilderContext();\n const kind = useSchematic((schematic) => schematic.kind);\n const collapsed = useSchematic((schematic) => schematic.collapsed);\n const Builder = schematicKinds.get(kind)?.builder;\n\n return Builder ? (\n <button\n type=\"button\"\n className=\"builder-child-marker\"\n title={collapsed ? \"Expand\" : \"Collapse\"}\n onClick={() =>\n setSchematic((schematic) => ({\n collapsed: !schematic.collapsed,\n }))\n }\n {...preventDrag}\n >\n {collapsed ? \"▸\" : \"▾\"}\n </button>\n ) : (\n <span className=\"builder-child-marker\">•</span>\n );\n}\n","import { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\nimport { preventDrag } from \"../../util/preventDrag\";\n\nexport function ChildRemove() {\n const { removeSchematic } = useSchematicBuilderContext();\n return (\n <button\n className=\"builder-icon-button builder-child-remove\"\n type=\"button\"\n title=\"Remove\"\n onClick={removeSchematic}\n {...preventDrag}\n >\n ×\n </button>\n );\n}\n","import { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\nimport { preventDrag } from \"../../util/preventDrag\";\n\nconst KIND_PLACEHOLDER = \"--KIND--\";\n\nexport function KindSelect() {\n const { schematicKinds, useSchematic, setSchematic, parentChildName } =\n useSchematicBuilderContext();\n const kind = useSchematic((schematic) => schematic.kind);\n\n return (\n <select\n className=\"builder-input builder-schema\"\n value={kind}\n onChange={(evt) => {\n const schematicKind = schematicKinds.get(evt.target.value);\n setSchematic({\n kind: evt.target.value,\n packageSuffix: schematicKind?.defaultPackageSuffix?.(\n parentChildName!,\n ),\n name: schematicKind?.defaultName?.(parentChildName!),\n collapsed: false,\n nullable: schematicKind?.nullable ?? schematicKind?.defaultNullable,\n children: schematicKind?.initChildren?.(parentChildName!),\n });\n }}\n required\n style={{\n width: `calc(${(kind || KIND_PLACEHOLDER).length}ch + 1.85rem)`,\n }}\n {...preventDrag}\n >\n <option>{KIND_PLACEHOLDER}</option>\n {Array.from(schematicKinds.values())\n .filter((kind) => !kind.internal)\n .map((kind) => (\n <option key={kind.kind} value={kind.kind}>\n {kind.kind}\n </option>\n ))}\n </select>\n );\n}\n","import { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\nimport { preventDrag } from \"../../util/preventDrag\";\n\nexport function NullableInput() {\n const { schematicKinds, useSchematic, setSchematic } =\n useSchematicBuilderContext();\n const kind = useSchematic((schematic) => schematic.kind);\n const nullable = useSchematic((schematic) => !!schematic.nullable);\n const schematicKind = schematicKinds.get(kind);\n const showInput = schematicKind && schematicKind.nullable === undefined;\n\n return (\n showInput && (\n <input\n className=\"builder-input builder-nullable\"\n type=\"checkbox\"\n title=\"Nullable?\"\n checked={nullable}\n onChange={(evt) => setSchematic({ nullable: evt.target.checked })}\n {...preventDrag}\n />\n )\n );\n}\n","import * as React from \"react\";\n\nimport { useSchematicBuilderContext } from \"../../SchematicBuilderContext\";\nimport { preventDrag } from \"../../util/preventDrag\";\n\nconst PACKAGE_PLACEHOLDER = \"package.suffix\";\nconst NAME_PLACEHOLDER = \"ClassName\";\n\nexport function PackageNameInput() {\n const { useSchematic, setSchematic, parentPackage } =\n useSchematicBuilderContext();\n const pkgRef = React.useRef<HTMLInputElement | null>(null);\n const packageSuffix = useSchematic((schematic) => schematic.packageSuffix);\n const name = useSchematic((schematic) => schematic.name);\n\n return (\n <div className=\"builder-package-name\">\n {parentPackage && (\n <span className=\"builder-package-prefix\">{parentPackage}.</span>\n )}\n <input\n className=\"builder-input builder-package\"\n placeholder={PACKAGE_PLACEHOLDER}\n value={packageSuffix ?? \"\"}\n onChange={(evt) => setSchematic({ packageSuffix: evt.target.value })}\n pattern=\"(([a-z_][a-z0-9_]*)(\\.([a-z_][a-z0-9_]*))*)?\"\n style={{ width: `${(packageSuffix || PACKAGE_PLACEHOLDER).length}ch` }}\n {...preventDrag}\n ref={pkgRef}\n />\n <button\n type=\"button\"\n className=\"builder-icon-button builder-edit-package\"\n onClick={() => {\n const pkgInput = pkgRef.current!;\n pkgInput.style.display = \"initial\";\n pkgInput.focus();\n pkgInput.style.display = \"\";\n }}\n >\n 🖉\n </button>\n <span className=\"builder-operator\">.</span>\n <input\n className=\"builder-input builder-name\"\n placeholder={NAME_PLACEHOLDER}\n value={name ?? \"\"}\n onChange={(evt) => setSchematic({ name: evt.target.value })}\n required\n pattern=\"[a-zA-Z_][a-zA-Z0-9_]*\"\n style={{ width: `${(name || NAME_PLACEHOLDER).length}ch` }}\n {...preventDrag}\n />\n </div>\n );\n}\n","import { useDraggable, useDroppable } from \"@dnd-kit/core\";\n\nimport { createSchematic, Schematic } from \"../Schematic\";\nimport {\n SchematicBuilderContext,\n useSchematicBuilderContext,\n} from \"../SchematicBuilderContext\";\nimport { boolDataAttr } from \"../util/boolDataAttr\";\nimport { joinPackages } from \"../util/join\";\nimport { preventDrag } from \"../util/preventDrag\";\nimport { ChildMarker } from \"./util/ChildMarker\";\nimport { ChildRemove } from \"./util/ChildRemove\";\nimport { KindSelect } from \"./util/KindSelect\";\nimport { NullableInput } from \"./util/NullableInput\";\nimport { PackageNameInput } from \"./util/PackageNameInput\";\n\nexport const CLASS_DND_TYPE = \"class-field\";\n\nexport function ClassBuilder() {\n const schematicBuilderContext = useSchematicBuilderContext();\n const { useSchematic, setSchematic, draggedSchematic, disableDrop } =\n schematicBuilderContext;\n const id = useSchematic((schematic) => schematic.id);\n const packageSuffix = useSchematic((schematic) => schematic.packageSuffix);\n const fields =\n useSchematic((schematic: Schematic) => schematic.children) ?? [];\n const shouldIgnoreDrop =\n draggedSchematic != null && draggedSchematic.id === fields.at(-1)?.id;\n const { setNodeRef: setDroppableRef, isOver } = useDroppable({\n id: `${id}-append`,\n data: { parentId: id, sortBefore: null, ignored: shouldIgnoreDrop },\n disabled: disableDrop || draggedSchematic?.type !== CLASS_DND_TYPE,\n });\n\n return (\n <div className=\"class-builder builder\">\n <PackageNameInput />\n\n {fields.length === 0 && (\n <div\n className=\"builder-empty\"\n data-over={boolDataAttr(isOver)}\n ref={setDroppableRef}\n >\n No fields.\n </div>\n )}\n\n {fields.length > 0 && (\n <ul className=\"builder-children\">\n {fields.map((field, i) => (\n <SchematicBuilderContext.Provider\n key={field.id}\n value={{\n ...schematicBuilderContext,\n useSchematic: (selector) =>\n useSchematic((schematic) => selector(schematic.children![i])),\n setSchematic: (toSet) =>\n setSchematic((schematic) => ({\n ...schematic,\n children: schematic.children!.map((f) =>\n field === f\n ? {\n ...f,\n ...(typeof toSet === \"function\" ? toSet(f) : toSet),\n }\n : f,\n ),\n })),\n removeSchematic: () =>\n setSchematic((schematic) => ({\n ...schematic,\n children: schematic.children!.filter((f) => f !== field),\n })),\n parentPackage: joinPackages(\n schematicBuilderContext.parentPackage,\n packageSuffix,\n ),\n parentId: id,\n ignoreDrop:\n draggedSchematic != null &&\n draggedSchematic.id === fields[i - 1]?.id,\n }}\n >\n <ClassFieldBuilder />\n </SchematicBuilderContext.Provider>\n ))}\n\n <li\n className=\"builder-child-droppable\"\n data-over={boolDataAttr(isOver && !shouldIgnoreDrop)}\n ref={setDroppableRef}\n />\n </ul>\n )}\n\n <button\n className=\"builder-input builder-new-child\"\n type=\"button\"\n onClick={() => {\n setSchematic((schematic) => ({\n ...schematic,\n children: [\n ...(schematic.children ?? []),\n createSchematic({ childName: \"\" }),\n ],\n }));\n }}\n {...preventDrag}\n >\n Add field\n </button>\n </div>\n );\n}\n\nconst CHILD_NAME_PLACEHOLDER = \"fieldName\";\n\nexport function ClassFieldBuilder() {\n const schematicBuilderContext = useSchematicBuilderContext();\n const {\n schematicKinds,\n useSchematic,\n setSchematic,\n draggedSchematic,\n parentId,\n disableDrop,\n ignoreDrop,\n } = schematicBuilderContext;\n const id = useSchematic((schematic) => schematic.id);\n const childName = useSchematic((schematic) => schematic.childName);\n const kind = useSchematic((schematic) => schematic.kind);\n const nullable = useSchematic((schematic) => schematic.nullable);\n const collapsed = useSchematic((schematic) => schematic.collapsed);\n const Builder = schematicKinds.get(kind)?.builder;\n\n const {\n attributes,\n listeners,\n setNodeRef: setDraggableRef,\n isDragging,\n } = useDraggable({\n id,\n data: {\n type: CLASS_DND_TYPE,\n node: (\n <>\n {[childName, kind].filter((s) => s).join(\": \")}\n {nullable ? \"?\" : \"\"}\n </>\n ),\n },\n });\n const shouldIgnoreDrop = isDragging || ignoreDrop;\n const { setNodeRef: setDroppableRef, isOver } = useDroppable({\n id,\n data: { parentId, sortBefore: id, ignored: shouldIgnoreDrop },\n disabled: disableDrop || draggedSchematic?.type !== CLASS_DND_TYPE,\n });\n\n return (\n <>\n <li\n className=\"builder-child-droppable\"\n data-over={boolDataAttr(isOver && !shouldIgnoreDrop)}\n ref={setDroppableRef}\n />\n\n <li\n className=\"builder-child class-builder-field\"\n data-dragging={boolDataAttr(isDragging)}\n {...listeners}\n {...attributes}\n ref={setDraggableRef}\n data-collapsible={boolDataAttr(Builder != null)}\n >\n <div className=\"builder-child-content\">\n <ChildMarker />\n <input\n className=\"builder-input class-builder-field-id\"\n placeholder={CHILD_NAME_PLACEHOLDER}\n value={childName}\n onChange={(evt) => setSchematic({ childName: evt.target.value })}\n required\n pattern=\"[a-zA-Z_][a-zA-Z0-9_]*\"\n style={{\n width: `${(childName || CHILD_NAME_PLACEHOLDER).length}ch`,\n }}\n {...preventDrag}\n />\n <span className=\"builder-operator\">∶</span>\n <SchematicBuilderContext.Provider\n value={{ ...schematicBuilderContext, parentChildName: childName }}\n >\n <KindSelect />\n </SchematicBuilderContext.Provider>\n <NullableInput />\n <ChildRemove />\n </div>\n\n {Builder && !collapsed && (\n <SchematicBuilderContext.Provider\n value={{\n ...schematicBuilderContext,\n parentChildName: childName,\n disableDrop: disableDrop || isDragging,\n }}\n >\n <Builder />\n </SchematicBuilderContext.Provider>\n )}\n </li>\n </>\n );\n}\n","import { useDraggable, useDroppable } from \"@dnd-kit/core\";\n\nimport { createSchematic, Schematic } from \"../Schematic\";\nimport {\n SchematicBuilderContext,\n useSchematicBuilderContext,\n} from \"../SchematicBuilderContext\";\nimport { boolDataAttr } from \"../util/boolDataAttr\";\nimport { preventDrag } from \"../util/preventDrag\";\nimport { ChildMarker } from \"./util/ChildMarker\";\nimport { ChildRemove } from \"./util/ChildRemove\";\nimport { PackageNameInput } from \"./util/PackageNameInput\";\n\nexport const ENUM_DND_TYPE = \"enum-entry\";\n\nexport function EnumBuilder() {\n const schematicBuilderContext = useSchematicBuilderContext();\n const { useSchematic, setSchematic, draggedSchematic, disableDrop } =\n schematicBuilderContext;\n const id = useSchematic((schematic) => schematic.id);\n const fields =\n useSchematic((schematic: Schematic) => schematic.children) ?? [];\n const shouldIgnoreDrop =\n draggedSchematic != null && draggedSchematic.id === fields.at(-1)?.id;\n const { setNodeRef: setDroppableRef, isOver } = useDroppable({\n id: `${id}-append`,\n data: { parentId: id, sortBefore: null, ignored: shouldIgnoreDrop },\n disabled: disableDrop || draggedSchematic?.type !== ENUM_DND_TYPE,\n });\n\n return (\n <div className=\"enum-builder builder\">\n <PackageNameInput />\n\n {fields.length === 0 && (\n <div\n className=\"builder-empty\"\n data-over={boolDataAttr(isOver)}\n ref={setDroppableRef}\n >\n No entries.\n </div>\n )}\n\n {fields.length > 0 && (\n <ul className=\"builder-children\">\n {fields.map((field, i) => (\n <SchematicBuilderContext.Provider\n key={field.id}\n value={{\n ...schematicBuilderContext,\n useSchematic: (selector) =>\n useSchematic((schematic) => selector(schematic.children![i])),\n setSchematic: (toSet) =>\n setSchematic((schematic) => ({\n ...schematic,\n children: schematic.children!.map((f) =>\n field === f\n ? {\n ...f,\n ...(typeof toSet === \"function\" ? toSet(f) : toSet),\n }\n : f,\n ),\n })),\n removeSchematic: () =>\n setSchematic((schematic) => ({\n ...schematic,\n children: schematic.children!.filter((f) => f !== field),\n })),\n parentId: id,\n ignoreDrop:\n draggedSchematic != null &&\n draggedSchematic.id === fields[i - 1]?.id,\n }}\n >\n <EnumEntryBuilder />\n </SchematicBuilderContext.Provider>\n ))}\n\n <li\n className=\"builder-child-droppable\"\n data-over={boolDataAttr(isOver && !shouldIgnoreDrop)}\n ref={setDroppableRef}\n />\n </ul>\n )}\n\n <button\n className=\"builder-input builder-new-child\"\n type=\"button\"\n onClick={() => {\n setSchematic((schematic) => ({\n ...schematic,\n children: [\n ...(schematic.children ?? []),\n createSchematic({ childName: \"\" }),\n ],\n }));\n }}\n {...preventDrag}\n >\n Add entry\n </button>\n </div>\n );\n}\n\nconst CHILD_NAME_PLACEHOLDER = \"ENTRY_NAME\";\n\nexport function EnumEntryBuilder() {\n const schematicBuilderContext = useSchematicBuilderContext();\n const {\n schematicKinds,\n useSchematic,\n setSchematic,\n draggedSchematic,\n parentId,\n disableDrop,\n ignoreDrop,\n } = schematicBuilderContext;\n const id = useSchematic((schematic) => schematic.id);\n const childName = useSchematic((schematic) => schematic.childName);\n const kind = useSchematic((schematic) => schematic.kind);\n const Builder = schematicKinds.get(kind)?.builder;\n\n const {\n attributes,\n listeners,\n setNodeRef: setDraggableRef,\n isDragging,\n } = useDraggable({\n id,\n data: {\n type: ENUM_DND_TYPE,\n node: <>{[childName, kind].filter((s) => s).join(\": \")}</>,\n },\n });\n const shouldIgnoreDrop = isDragging || ignoreDrop;\n const { setNodeRef: setDroppableRef, isOver } = useDroppable({\n id,\n data: { parentId, sortBefore: id, ignored: shouldIgnoreDrop },\n disabled: disableDrop || draggedSchematic?.type !== ENUM_DND_TYPE,\n });\n\n return (\n <>\n <li\n className=\"builder-child-droppable\"\n data-over={boolDataAttr(isOver && !shouldIgnoreDrop)}\n ref={setDroppableRef}\n />\n\n <li\n className=\"builder-child\"\n data-dragging={boolDataAttr(isDragging)}\n {...listeners}\n {...attributes}\n ref={setDraggableRef}\n data-collapsible={boolDataAttr(Builder != null)}\n >\n <div className=\"builder-child-content\">\n <ChildMarker />\n <input\n className=\"builder-input enum-builder-entry-id\"\n placeholder={CHILD_NAME_PLACEHOLDER}\n value={childName}\n onChange={(evt) => setSchematic({ childName: evt.target.value })}\n required\n pattern=\"[a-zA-Z_][a-zA-Z0-9_]*\"\n style={{\n width: `${(childName || CHILD_NAME_PLACEHOLDER).length}ch`,\n }}\n {...preventDrag}\n />\n <ChildRemove />\n </div>\n </li>\n </>\n );\n}\n","import {\n SchematicBuilderContext,\n useSchematicBuilderContext,\n} from \"../SchematicBuilderContext\";\nimport { KindSelect } from \"./util/KindSelect\";\n\nexport interface ListableBuilderProps {\n showKindSelect?: boolean;\n}\n\nexport function ListableBuilder({\n showKindSelect = true,\n}: ListableBuilderProps) {\n const schematicBuilderContext = useSchematicBuilderContext();\n const { useSchematic, setSchematic } = schematicBuilderContext;\n const id = useSchematic((schematic) => schematic.id);\n\n return (\n <div className=\"listable-builder builder\">\n <SchematicBuilderContext.Provider\n value={{\n ...schematicBuilderContext,\n useSchematic: (selector) =>\n useSchematic((schematic) => selector(schematic.children![0])),\n setSchematic: (toSet) =>\n setSchematic((schematic) => ({\n ...schematic,\n children: [\n {\n ...schematic.children![0],\n ...(typeof toSet === \"function\"\n ? toSet(schematic.children![0])\n : toSet),\n },\n ],\n })),\n removeSchematic: () => {},\n parentId: id,\n }}\n >\n <ListableItemBuilder showKindSelect={showKindSelect} />\n </SchematicBuilderContext.Provider>\n </div>\n );\n}\n\nexport function ListableBuilderWithoutKindSelect() {\n return <ListableBuilder showKindSelect={false} />;\n}\n\nexport type ListableItemBuilderProps = ListableBuilderProps;\n\nexport function ListableItemBuilder({\n showKindSelect = true,\n}: ListableItemBuilderProps) {\n const { schematicKinds, useSchematic } = useSchematicBuilderContext();\n const kind = useSchematic((schematic) => schematic.kind);\n const Builder = schematicKinds.get(kind)?.builder;\n\n return (\n <>\n {showKindSelect && <KindSelect />}\n {Builder && <Builder />}\n </>\n );\n}\n","const LF = /\\r?\\n/;\nconst EMPTY_LINE = /^\\s*$/;\nconst SPACE = /\\s/;\nconst LF_AFFIX = /(^\\r?\\n)|(\\r?\\n$)/g;\n\n/** Tag function used to aid in the construction of code blocks. */\nexport function code(strings: TemplateStringsArray, ...values: any[]): string {\n const minIndent = Math.min(\n ...strings.join(\"\").split(LF).filter(isNotEmpty).map(indentWidth),\n );\n\n let result = \"\";\n for (let i = 0; i < values.length; ++i) {\n const trimmedString = trimNoninitialLines(strings[i], minIndent);\n result += trimmedString;\n result += indentNoninitialLines(\n String(values[i]),\n indentWidth(trimmedString.split(LF).at(-1)!),\n );\n }\n result += trimNoninitialLines(strings.at(-1)!, minIndent);\n return result.replace(LF_AFFIX, \"\");\n}\n\nfunction isNotEmpty(line: string): boolean {\n return !EMPTY_LINE.test(line);\n}\n\nfunction indentWidth(line: string): number {\n let width = 0;\n for (const c of line) {\n if (!SPACE.test(c)) {\n break;\n }\n ++width;\n }\n return width;\n}\n\nfunction indentNoninitialLines(str: string, indentWidth: number): string {\n const lines = str.split(LF);\n const resultLines: string[] = [lines[0]];\n for (let i = 1; i < lines.length; ++i) {\n const line = lines[i];\n resultLines.push(line && \" \".repeat(indentWidth) + line);\n }\n return resultLines.join(\"\\n\");\n}\n\nfunction trimNoninitialLines(str: string, indentWidth: number): string {\n const lines = str.split(LF);\n const resultLines: string[] = [lines[0]];\n for (let i = 1; i < lines.length; ++i) {\n resultLines.push(lines[i].slice(indentWidth));\n }\n return resultLines.join(\"\\n\");\n}\n","import { pascalCase } from \"change-case\";\nimport * as React from \"react\";\n\nimport { KtData } from \"./scaffolding/kotlin/KtData\";\nimport { annotate } from \"./scaffolding/kotlin/util/annotations\";\nimport {\n scaffoldDefaultValue,\n scaffoldPropertyAnnotations,\n scaffoldType,\n} from \"./scaffolding/scaffolders/scaffoldModels\";\nimport { scaffoldSchema } from \"./scaffolding/scaffolders/scaffoldSchemas\";\nimport { createSchematic, Schematic } from \"./Schematic\";\nimport { ClassBuilder } from \"./schematicBuilders/ClassBuilder\";\nimport { EnumBuilder } from \"./schematicBuilders/EnumBuilder\";\nimport { ListableBuilder } from \"./schematicBuilders/ListableBuilder\";\nimport { code } from \"./util/code\";\nimport { joinPaths } from \"./util/join\";\n\nexport const NULLABLE_SCHEMA = \"io.kform.schemas.NullableSchema\";\n\n/** Information about a kind of schematic. */\nexport interface SchematicKind {\n kind: string;\n internal?: boolean;\n schema: string;\n package?: string;\n name?: string;\n defaultNullable?: boolean;\n nullable?: boolean;\n defaultValue: string | ((schematic: Schematic, data: KtData) => string);\n propertyAnnotations?:\n | string\n | string[]\n | ((schematic: Schematic, data: KtData) => string | string[] | undefined);\n builder?: React.ComponentType;\n initChildren?: (childName: string) => Schematic[];\n defaultPackageSuffix?: (childName: string) => string;\n defaultName?: (childName: string) => string;\n scaffoldModel?: (schematic: Schematic, data: KtData) => string;\n scaffoldType?: (schematic: Schematic, data: KtData) => string;\n scaffoldSchema?: (schematic: Schematic, data: KtData) => string;\n}\n\nexport const anySchematicKind: SchematicKind = {\n kind: \"Any\",\n package: \"kotlin\",\n name: \"Any\",\n schema: \"io.kform.schemas.AnySchema\",\n nullable: false,\n defaultValue: \"null\",\n scaffoldType: () => \"Any?\",\n};\nexport const bigDecimalSchematicKind: SchematicKind = {\n kind: \"BigDecimal\",\n package: \"io.kform.datatypes\",\n name: \"BigDecimal\",\n schema: \"io.kform.schemas.BigDecimalSchema\",\n defaultNullable: true,\n defaultValue: \"BigDecimal.ZERO\",\n};\nexport const bigIntegerSchematicKind: SchematicKind = {\n kind: \"BigInteger\",\n package: \"io.kform.datatypes\",\n name: \"BigInteger\",\n schema: \"io.kform.schemas.BigIntegerSchema\",\n defaultNullable: true,\n defaultValue: \"BigInteger.ZERO\",\n};\nexport const booleanSchematicKind: SchematicKind = {\n kind: \"Boolean\",\n package: \"kotlin\",\n name: \"Boolean\",\n schema: \"io.kform.schemas.BooleanSchema\",\n defaultValue: \"false\",\n};\nexport const byteSchematicKind: SchematicKind = {\n kind: \"Byte\",\n package: \"kotlin\",\n name: \"Byte\",\n schema: \"io.kform.schemas.ByteSchema\",\n defaultNullable: true,\n defaultValue: \"0.toByte()\",\n};\nexport const charSchematicKind: SchematicKind = {\n kind: \"Char\",\n package: \"kotlin\",\n name: \"Char\",\n schema: \"io.kform.schemas.CharSchema\",\n defaultNullable: true,\n defaultValue: \"0.toChar()\",\n};\nexport const classSchematicKind: SchematicKind = {\n kind: \"Class\",\n schema: \"io.kform.schemas.ClassSchema\",\n defaultValue: (schematic) => `${schematic.name}()`,\n builder: ClassBuilder,\n initChildren: () => [],\n defaultPackageSuffix: (childName: string) => childName.toLowerCase(),\n defaultName: (childName: string) => pascalCase(childName),\n scaffoldModel: (schematic, data) =>\n code`\n data class ${schematic.name}(\n ${schematic.children\n ?.map((childSchematic) => {\n const childData = {\n ...data,\n currentPath: joinPaths(\n data.currentPath,\n childSchematic.childName,\n ),\n };\n\n return annotate(\n scaffoldPropertyAnnotations(childSchematic, childData),\n `var ${childSchematic.childName}: ${scaffoldType(\n childSchematic,\n childData,\n )} = ${scaffoldDefaultValue(childSchematic, childData)}`,\n );\n })\n .join(\",\\n\")}\n )\n `,\n scaffoldSchema: (schematic, data) =>\n code`\n ClassSchema {\n ${schematic.children\n ?.map(\n (childSchematic) =>\n `${schematic.name}::${childSchematic.childName} { ${scaffoldSchema(\n childSche