@ostack.tech/ui-kform-scaffolder 0.3.4 → 0.4.0

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.
@@ -139,78 +139,55 @@ function scaffoldIssueMessagesConstant(schematic, data) {
139
139
  export const ${constantCase(schematic.name)}_ISSUE_MESSAGES: Record<string, IssueMessagesByCode> = {};
140
140
  `);
141
141
  }
142
- function scaffoldFormPages(schematic, data) {
142
+ function scaffoldPageComponents(schematic, data) {
143
143
  const layout = reactAppLayout(schematic, {
144
144
  kmpModuleName: `${kebabCase(schematic.name)}-shared`,
145
145
  ...data
146
146
  });
147
147
  for (const annex of layout) {
148
148
  for (const formPage of annex.formPages) {
149
- scaffoldFormPage(formPage.schematic, formPage.data);
149
+ scaffoldPageComponent(formPage.schematic, formPage.data);
150
150
  if (formPage.data.currentPath !== "/") {
151
151
  scaffoldIssueMessages(formPage.schematic, formPage.data);
152
152
  }
153
153
  }
154
154
  }
155
155
  }
156
- function scaffoldFormPage(schematic, data) {
156
+ function scaffoldPageComponent(schematic, data) {
157
157
  const fileName = joinPaths(data.currentDir, `${schematic.name}.tsx`);
158
158
  let file = data.files.get(fileName);
159
159
  if (!file) {
160
160
  data.files.set(fileName, file = tsFile());
161
161
  }
162
- scaffoldFormPageComponent(schematic, {
162
+ scaffoldPageComponentDeclarations(schematic, {
163
163
  kmpModuleName: `${kebabCase(data.rootSchematic.name)}-shared`,
164
164
  ...data,
165
165
  currentFile: file
166
166
  });
167
167
  }
168
- function scaffoldFormPageComponent(schematic, data) {
169
- data.currentFile.imports.push(
170
- { moduleName: "@ostack.tech/ui-kform", name: "FormPage" },
171
- { moduleName: "@ostack.tech/ui", name: "Grid" }
172
- );
173
- if (data.currentPath !== "/") {
174
- data.currentFile.imports.push({
175
- moduleName: `./${schematic.name}IssueMessages.tsx`,
176
- name: `${constantCase(schematic.name)}_ISSUE_MESSAGES`
177
- });
178
- }
179
- let pathString;
180
- let preamble;
181
- const splitPath = data.currentPath.split("/");
182
- if (splitPath.some((fragment) => fragment === "*")) {
183
- data.currentFile.imports.push({
184
- moduleName: "react-router",
185
- name: "useParams"
186
- });
187
- pathString = `{\`${splitPath.map((f) => f === "*" ? "${id}" : f).join("/")}\`}`;
188
- preamble = "\n const { id } = useParams();\n";
189
- } else {
190
- pathString = `"${data.currentPath}"`;
191
- preamble = "";
192
- }
193
- const pageComponent = code`
194
- export function ${schematic.name}() {${preamble}
168
+ function scaffoldPageComponentDeclarations(schematic, data) {
169
+ data.currentFile.imports.push({
170
+ moduleName: "@ostack.tech/ui",
171
+ name: "Grid"
172
+ });
173
+ data.currentFile.declarations.push(code`
174
+ export function ${schematic.name}() {
195
175
  return (
196
- <FormPage${data.currentPath === "/" ? "" : ` path=${pathString} issueMessages={${constantCase(schematic.name)}_ISSUE_MESSAGES}`}>
197
- <Grid container>
198
- ${schematic.children.map(
176
+ <Grid container>
177
+ ${schematic.children.map(
199
178
  (child) => code`
200
- <Grid xs={12}>
201
- ${scaffoldField(child, {
179
+ <Grid xs={12}>
180
+ ${scaffoldField(child, {
202
181
  ...data,
203
182
  currentPath: child.childName
204
183
  })}
205
- </Grid>
206
- `
184
+ </Grid>
185
+ `
207
186
  ).join("\n\n")}
208
- </Grid>
209
- </FormPage>
187
+ </Grid>
210
188
  );
211
189
  }
212
- `;
213
- data.currentFile.declarations.push(pageComponent);
190
+ `);
214
191
  }
215
192
  function scaffoldField(schematic, data) {
216
193
  const schematicKind = data.schematicKinds.get(
@@ -753,6 +730,7 @@ function layoutAnnex(schematic, data) {
753
730
  }
754
731
  return { schematic, data, formPages };
755
732
  }
733
+ const DEFAULT_ACTIVE_PATH_SEARCH_PARAM = "activePath";
756
734
  function scaffoldAppComponent(schematic, data) {
757
735
  const fileName = joinPaths(data.currentDir, `${schematic.name}App.tsx`);
758
736
  let file = data.files.get(fileName);
@@ -768,10 +746,18 @@ function scaffoldAppComponent(schematic, data) {
768
746
  }
769
747
  function scaffoldAppComponentFile(schematic, data) {
770
748
  const localeVar = (data.defaultLocale ?? DEFAULT_LOCALE).replaceAll("-", "");
749
+ const activePathSearchParam = data.activePathSearchParam ?? DEFAULT_ACTIVE_PATH_SEARCH_PARAM;
771
750
  data.currentFile.imports.push(
772
751
  { moduleName: "@ostack.tech/kform", name: "ktMapToObject" },
752
+ { moduleName: "@ostack.tech/ui", name: "useSearchParam" },
773
753
  { moduleName: "@ostack.tech/ui-kform", name: "FormApp" },
774
754
  { moduleName: "@ostack.tech/ui-kform", name: localeVar },
755
+ { moduleName: "@ostack.tech/ui-kform", name: "TopBar" },
756
+ { moduleName: "@ostack.tech/ui-kform", name: "TopBarActions" },
757
+ { moduleName: "@ostack.tech/ui-kform", name: "Annexes" },
758
+ { moduleName: "@ostack.tech/ui-kform", name: "Annex" },
759
+ { moduleName: "@ostack.tech/ui-kform", name: "FormPages" },
760
+ { moduleName: "@ostack.tech/ui-kform", name: "IssuesPanel" },
775
761
  { moduleName: "react", name: "useMemo" },
776
762
  { moduleName: data.kmpModuleName, name: schematic.name },
777
763
  { moduleName: data.kmpModuleName, name: `${schematic.name}FormMode` },
@@ -784,11 +770,27 @@ function scaffoldAppComponentFile(schematic, data) {
784
770
  {
785
771
  moduleName: `./${schematic.name}IssueMessages.tsx`,
786
772
  name: `${constantCase(schematic.name)}_ISSUE_MESSAGES`
787
- }
773
+ },
774
+ { moduleName: "./actions/Load.tsx", name: "Load" },
775
+ { moduleName: "./actions/Save.tsx", name: "Save" },
776
+ { moduleName: "./actions/Validate.tsx", name: "Validate" },
777
+ { moduleName: "./actions/Submit.tsx", name: "Submit" }
788
778
  );
779
+ const layout = reactAppLayout(schematic, data);
780
+ let annexesManager = "";
781
+ if (layout.length > 1) {
782
+ data.currentFile.imports.push({
783
+ moduleName: "@ostack.tech/ui-kform",
784
+ name: "AnnexesManager"
785
+ });
786
+ annexesManager = `
787
+ <AnnexesManager />
788
+ `;
789
+ }
789
790
  data.currentFile.declarations.push(code`
790
791
  export function ${schematic.name}App() {
791
792
  const { externalContexts } = use${schematic.name}Context();
793
+ const [activePath, setActivePath] = useSearchParam("${activePathSearchParam}");
792
794
 
793
795
  return (
794
796
  <FormApp
@@ -807,79 +809,13 @@ function scaffoldAppComponentFile(schematic, data) {
807
809
  ? "manual"
808
810
  : "auto"
809
811
  }
812
+ activePath={activePath}
813
+ onActivePathChange={setActivePath}
810
814
  issueMessages={${constantCase(schematic.name)}_ISSUE_MESSAGES}
811
815
  defaultLocale={${localeVar}}
812
816
  reportError={reportError}
813
817
  >
814
- <${schematic.name}Layout />
815
- </FormApp>
816
- );
817
- }
818
- `);
819
- const layout = reactAppLayout(schematic, data);
820
- data.currentFile.imports.push(
821
- { moduleName: "react-router", name: "useNavigate" },
822
- { moduleName: "react-router", name: "useLocation" },
823
- { moduleName: "react-router", name: "Outlet" },
824
- { moduleName: "@ostack.tech/ui-kform", name: "Annexes" },
825
- { moduleName: "@ostack.tech/ui-kform", name: "Annex" },
826
- {
827
- moduleName: "@ostack.tech/ui-kform",
828
- name: "AnnexObject",
829
- isType: true
830
- },
831
- { moduleName: "@ostack.tech/ui-kform", name: "TopBar" },
832
- { moduleName: "@ostack.tech/ui-kform", name: "TopBarActions" },
833
- { moduleName: "./actions/Load.tsx", name: "Load" },
834
- { moduleName: "./actions/Save.tsx", name: "Save" },
835
- { moduleName: "./actions/Validate.tsx", name: "Validate" },
836
- { moduleName: "./actions/Submit.tsx", name: "Submit" },
837
- { moduleName: "@ostack.tech/ui-kform", name: "FormPages" },
838
- {
839
- moduleName: "@ostack.tech/ui-kform",
840
- name: "FormPageObject",
841
- isType: true
842
- },
843
- { moduleName: "@ostack.tech/ui-kform", name: "IssuesPanel" }
844
- );
845
- let annexesManager = "";
846
- if (layout.length > 1) {
847
- data.currentFile.imports.push({
848
- moduleName: "@ostack.tech/ui-kform",
849
- name: "AnnexesManager"
850
- });
851
- annexesManager = `
852
- <AnnexesManager />
853
- `;
854
- }
855
- let formPagesNavigation = "";
856
- if (layout.some((annex) => annex.formPages.length > 1)) {
857
- data.currentFile.imports.push({
858
- moduleName: "@ostack.tech/ui-kform",
859
- name: "FormPagesNavigation"
860
- });
861
- if (layout.every((annex) => annex.formPages.length > 1)) {
862
- formPagesNavigation = `
863
- <FormPagesNavigation />
864
- `;
865
- } else {
866
- formPagesNavigation = `
867
- {pages.length > 1 && <FormPagesNavigation />}
868
- `;
869
- }
870
- }
871
- data.currentFile.declarations.push(code`
872
- function ${schematic.name}Layout() {
873
- const navigate = useNavigate();
874
- const { pathname } = useLocation();
875
-
876
- const annexes: AnnexObject[] = useMemo(() => ${scaffoldAnnexesArray(layout)}, []);
877
-
878
- const pagesByAnnex: Record<string, FormPageObject[]> = useMemo(() => (${scaffoldFormPagesByAnnexObject(layout)}), []);
879
-
880
- return (
881
- <>
882
- <Annexes annexes={annexes} activeAnnex={pathname}>
818
+ <Annexes>
883
819
  <TopBar>${annexesManager}
884
820
  <TopBarActions>
885
821
  <Load />
@@ -889,64 +825,70 @@ function scaffoldAppComponentFile(schematic, data) {
889
825
  </TopBarActions>
890
826
  </TopBar>
891
827
 
892
- {Object.entries(pagesByAnnex).map(([annexPath, pages]) => (
893
- <Annex path={annexPath} key={annexPath}>
894
- <FormPages
895
- pages={pages}
896
- activePage={pathname}
897
- onActivePageChange={(activePage) => {
898
- const newPathname = activePage?.toString() ?? "/";
899
- if (pathname !== newPathname) {
900
- void navigate(newPathname, {
901
- replace: newPathname.startsWith(
902
- pathname.endsWith("/") ? pathname : \`\${pathname}/\`,
903
- )
904
- });
905
- }
906
- }}
907
- >${formPagesNavigation}
908
- <Outlet />
909
- </FormPages>
910
- </Annex>
911
- ))}
828
+ ${scaffoldAnnexes(layout, data)}
912
829
  </Annexes>
913
830
 
914
831
  <IssuesPanel />
915
- </>
832
+ </FormApp>
916
833
  );
917
834
  }
918
835
  `);
919
836
  }
920
- function scaffoldAnnexesArray(layout) {
837
+ function scaffoldAnnexes(layout, data) {
838
+ return layout.map((annex) => scaffoldAnnex(annex, data)).join("\n\n");
839
+ }
840
+ function scaffoldAnnex(annex, data) {
841
+ let formPagesNavigation = "";
842
+ if (annex.formPages.length > 1) {
843
+ data.currentFile.imports.push({
844
+ moduleName: "@ostack.tech/ui-kform",
845
+ name: "FormPagesNavigation"
846
+ });
847
+ formPagesNavigation = `
848
+ <FormPagesNavigation />
849
+ `;
850
+ }
921
851
  return code`
922
- [
923
- ${layout.map(
924
- (annex) => `{ path: "${annex.data.currentPath}", title: "${sentenceCase(
925
- annex.schematic.name
926
- )}"${annex.formPages.length === 1 && annex.data.currentPath === annex.formPages[0].data.currentPath ? ", documentTitle: null" : ""} }`
927
- ).join(",\n")}
928
- ]
852
+ <Annex path="${annex.data.currentPath}" title="${sentenceCase(annex.schematic.name)}"${annex.formPages.length === 1 && annex.schematic === annex.formPages[0].schematic ? " documentTitle={null}" : ""}>
853
+ <FormPages>${formPagesNavigation}
854
+ ${annex.formPages.map((formPage) => scaffoldFormPage(annex, formPage, data))}
855
+ </FormPages>
856
+ </Annex>
929
857
  `;
930
858
  }
931
- function scaffoldFormPagesByAnnexObject(layout) {
932
- return code`
933
- {
934
- ${layout.map(
935
- (annex) => `"${annex.data.currentPath}": ` + code`
936
- [
937
- ${annex.formPages.map(
938
- (formPage) => annex.schematic === formPage.schematic ? `{ title: "${sentenceCase(
939
- formPage.schematic.name
940
- )}", code: "${formPage.schematic.name?.[0].toUpperCase()}"${formPage.data.currentPath === "/" ? ", documentTitle: null" : ""} }` : `{ path: "${formPage.data.currentPath.slice(
941
- annex.data.currentPath.length + (annex.data.currentPath.endsWith("/") ? 0 : 1)
942
- )}", title: "${sentenceCase(
859
+ function scaffoldFormPage(annex, formPage, data) {
860
+ data.currentFile.imports.push({
861
+ moduleName: `./${joinPaths(
862
+ formPage.data.currentDir.slice(
863
+ data.currentDir.length + (data.currentDir.endsWith("/") ? 0 : 1)
864
+ ),
865
+ formPage.schematic.name
866
+ )}.tsx`,
867
+ name: formPage.schematic.name
868
+ });
869
+ let issueMessagesProp = "";
870
+ if (formPage.data.currentPath !== "/") {
871
+ data.currentFile.imports.push({
872
+ moduleName: `./${joinPaths(
873
+ formPage.data.currentDir.slice(
874
+ data.currentDir.length + (data.currentDir.endsWith("/") ? 0 : 1)
875
+ ),
943
876
  formPage.schematic.name
944
- )}", code: "${formPage.schematic.name?.[0].toUpperCase()}" }`
945
- ).join(",\n")}
946
- ]
947
- `
948
- ).join(",\n")}
949
- }
877
+ )}IssueMessages.tsx`,
878
+ name: `${constantCase(formPage.schematic.name)}_ISSUE_MESSAGES`
879
+ });
880
+ issueMessagesProp = ` issueMessages={${constantCase(formPage.schematic.name)}_ISSUE_MESSAGES}`;
881
+ }
882
+ const pathProp = annex.schematic === formPage.schematic ? "" : ` path="${formPage.data.currentPath.slice(
883
+ annex.data.currentPath.length + (annex.data.currentPath.endsWith("/") ? 0 : 1)
884
+ )}"`;
885
+ const documentTitleProp = formPage.data.currentPath === "/" ? " documentTitle={null}" : "";
886
+ return code`
887
+ <FormPage${pathProp} title="${sentenceCase(
888
+ formPage.schematic.name
889
+ )}" code="${formPage.schematic.name?.[0].toUpperCase()}"${documentTitleProp}${issueMessagesProp}>
890
+ <${formPage.schematic.name} />
891
+ </FormPage>
950
892
  `;
951
893
  }
952
894
  const contextTsEjs = 'import { createContext, useContext } from "react";\nimport { <%= formClass %>ExternalContexts } from "<%= kmpModuleName %>";\n\nexport interface <%= formClass %>ContextValue {\n externalContexts: <%= formClass %>ExternalContexts;\n}\n\nexport const <%= formClass %>Context = createContext<<%= formClass %>ContextValue>(null as never);\n\nexport function use<%= formClass %>Context(): <%= formClass %>ContextValue {\n return useContext(<%= formClass %>Context);\n}\n';
@@ -980,7 +922,7 @@ const gradlePropertiesEjs = "group=<%= gradlePropertiesGroup %>\nversion=0.0.1\n
980
922
  const libsVersionsTomlEjs = '[versions]\n# Plugins\nkotlin = "2.2.21"\nnodeGradle = "7.1.0"\n# Libraries\nkotlinxCoroutines = "1.10.2"\nkform = "<%= kFormVersion %>"\n<%_ if (includeKotlinxDatetime) { -%>\nkotlinxDatetime = "0.7.1"\n<%_ } -%>\n<%_ if (serializationFormat === "json") { -%>\nkotlinxSerialization = "1.9.0"\n<%_ } -%>\n<%_ if (includeKtMath) { -%>\nktMath = "0.10.2"\n<%_ } -%>\nslf4j = "2.0.17"\n<%_ if (serializationFormat === "xml") { -%>\nxmlutil = "0.91.1"\n<%_ } -%>\n# Runtimes\njvmToolchain = "<%= jvmToolchainVersion %>"\nnodejs = "<%= nodejsVersion %>"\n\n[libraries]\nkform = { module = "tech.ostack:kform", version.ref = "kform" }\nkform-jsBindings = { module = "tech.ostack:kform-js-bindings", version.ref = "kform" }\nkotlinxCoroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }\n<%_ if (includeKotlinxDatetime) { -%>\nkotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }\n<%_ } -%>\n<%_ if (serializationFormat === "json") { -%>\nkotlinxSerialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }\n<%_ } -%>\n<%_ if (includeKtMath) { -%>\nktMath = { module = "io.github.gciatto:kt-math", version.ref = "ktMath" }\n<%_ } -%>\nslf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }\n<%_ if (serializationFormat === "xml") { -%>\nxmlutil-serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "xmlutil" }\n<%_ } -%>\n\n[plugins]\nkotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }\nkotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }\nnodeGradle = { id = "com.github.node-gradle.node", version.ref = "nodeGradle" }\n';
981
923
  const reactAppBuildGradleKtsEjs = 'import com.github.gradle.node.npm.task.NpmTask\n\nplugins {\n alias(libs.plugins.nodeGradle)\n base\n}\n\nval isCI = System.getenv("CI") !in arrayOf(null, "0", "false")\nnode {\n download = true\n version = libs.versions.nodejs.get()\n npmInstallCommand = if (isCI) "ci" else "install"\n}\n\nval sharedPackage = project(":shared").file("build/<%= formId %>-shared-$version.tgz")\n\nval npmInstallShared by tasks.registering(NpmTask::class) {\n group = "build"\n dependsOn(":shared:packJsPackage")\n npmCommand = listOf("install", "<%= formId %>-shared@file:$sharedPackage")\n\n inputs.file(sharedPackage)\n outputs.dir("node_modules/<%= formId %>-shared")\n}\n\ntasks.npmInstall {\n dependsOn(npmInstallShared)\n}\n\nval npmRunDev by tasks.registering(NpmTask::class) {\n group = "development"\n dependsOn(tasks.npmInstall)\n npmCommand = listOf("run", "dev")\n}\n\nprivate fun NpmTask.npmRunBuild(buildReason: String) {\n group = "build"\n dependsOn(tasks.npmInstall)\n npmCommand = listOf("run", "build")\n environment = mapOf("VITE_BUILD_REASON" to buildReason)\n\n inputs.dir("src")\n inputs.file(sharedPackage)\n inputs.file("index.html")\n inputs.file("package.json")\n inputs.file("tsconfig.json")\n inputs.file("tsconfig.app.json")\n inputs.file("tsconfig.node.json")\n inputs.file("vite.config.ts")\n outputs.dir("build/dist")\n}\n\nval npmRunBuild by tasks.registering(NpmTask::class) {\n npmRunBuild("production")\n}\n\nval npmRunBuildPreview by tasks.registering(NpmTask::class) {\n npmRunBuild("preview")\n}\n\nval npmRunPreview by tasks.registering(NpmTask::class) {\n group = "development"\n dependsOn(npmRunBuildPreview)\n npmCommand = listOf("run", "preview")\n}\n\nval npmRunLint by tasks.registering(NpmTask::class) {\n group = "verification"\n dependsOn(tasks.npmInstall)\n npmCommand = listOf("run", "lint")\n\n inputs.dir("src")\n inputs.file("eslint.config.js")\n inputs.file("package.json")\n inputs.file("tsconfig.json")\n inputs.file("tsconfig.app.json")\n inputs.file("tsconfig.node.json")\n outputs.upToDateWhen { true }\n}\n\ntasks.assemble {\n dependsOn(npmRunBuild)\n}\n';
982
924
  const settingsGradleKtsEjs = 'plugins {\n id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"\n}\n\nrootProject.name = "<%= formId %>"\n\ninclude("shared", "react-app")\n';
983
- const sharedBuildGradleKtsEjs = 'import com.github.gradle.node.npm.task.NpmTask\n\nplugins {\n alias(libs.plugins.kotlin.multiplatform)\n alias(libs.plugins.kotlin.serialization)\n alias(libs.plugins.nodeGradle)\n}\n\nkotlin {\n jvmToolchain(libs.versions.jvmToolchain.get().toInt())\n jvm {\n testRuns["test"].executionTask.configure { useJUnitPlatform() }\n }\n js {\n compilerOptions {\n target = "es2015"\n freeCompilerArgs.add("-Xes-long-as-bigint")\n freeCompilerArgs.add("-Xwarning-level=NON_EXPORTABLE_TYPE:disabled")\n }\n binaries.library()\n browser { testTask { useMocha() } }\n generateTypeScriptDefinitions()\n }\n\n sourceSets {\n all {\n // Opt in to the experimental `JsExport` annotation\n languageSettings.optIn("kotlin.js.ExperimentalJsExport")\n }\n\n commonMain.dependencies {\n api(libs.kform)\n <%_ if (includeKotlinxDatetime) { -%>\n api(libs.kotlinxDatetime)\n <%_ } -%>\n <%_ if (serializationFormat === "json") { -%>\n api(libs.kotlinxSerialization.json)\n <%_ } -%>\n <%_ if (includeKtMath) { -%>\n api(libs.ktMath)\n <%_ } -%>\n <%_ if (serializationFormat === "xml") { -%>\n api(libs.xmlutil.serialization)\n <%_ } -%>\n }\n commonTest.dependencies {\n implementation(kotlin("test"))\n implementation(libs.kotlinxCoroutines.test)\n }\n jvmTest.dependencies {\n implementation(libs.slf4j.simple)\n }\n jsMain.dependencies {\n implementation(libs.kform.jsBindings)\n <%_ if (includeKotlinxDatetime) { -%>\n implementation(npm("@js-joda/timezone", "2.3.0"))\n <%_ } -%>\n }\n }\n}\n\nval isCI = System.getenv("CI") !in arrayOf(null, "0", "false")\nnode {\n download = true\n version = libs.versions.nodejs.get()\n npmInstallCommand = if (isCI) "ci" else "install"\n}\n\n// Packs the result of the JS compilation, for consumption by the React app\nval packJsPackage by tasks.registering(NpmTask::class) {\n group = "build"\n dependsOn("jsBrowserProductionLibraryDistribution")\n\n val libraryDir = "build/dist/js/productionLibrary"\n npmCommand = listOf("pack", libraryDir, "--pack-destination", "build")\n\n inputs.dir(libraryDir)\n outputs.file("build/<%= formId %>-shared-${version}.tgz")\n}\n';
925
+ const sharedBuildGradleKtsEjs = 'import com.github.gradle.node.npm.task.NpmTask\n\nplugins {\n alias(libs.plugins.kotlin.multiplatform)\n alias(libs.plugins.kotlin.serialization)\n alias(libs.plugins.nodeGradle)\n}\n\nkotlin {\n jvmToolchain(libs.versions.jvmToolchain.get().toInt())\n jvm {\n testRuns["test"].executionTask.configure { useJUnitPlatform() }\n }\n js {\n compilerOptions {\n target = "es2015"\n freeCompilerArgs.add("-Xes-long-as-bigint")\n freeCompilerArgs.add("-Xwarning-level=NON_EXPORTABLE_TYPE:disabled")\n }\n binaries.library()\n browser { testTask { useMocha() } }\n generateTypeScriptDefinitions()\n }\n\n sourceSets {\n all {\n // Opt in to the experimental `JsExport` annotation\n languageSettings.optIn("kotlin.js.ExperimentalJsExport")\n }\n\n commonMain.dependencies {\n api(libs.kform)\n <%_ if (includeKotlinxDatetime) { -%>\n api(libs.kotlinxDatetime)\n <%_ } -%>\n <%_ if (serializationFormat === "json") { -%>\n api(libs.kotlinxSerialization.json)\n <%_ } -%>\n <%_ if (includeKtMath) { -%>\n api(libs.ktMath)\n <%_ } -%>\n <%_ if (serializationFormat === "xml") { -%>\n api(libs.xmlutil.serialization)\n <%_ } -%>\n }\n commonTest.dependencies {\n implementation(kotlin("test"))\n implementation(libs.kotlinxCoroutines.test)\n }\n jvmTest.dependencies {\n implementation(libs.slf4j.simple)\n }\n jsMain.dependencies {\n implementation(libs.kform.jsBindings)\n <%_ if (includeKotlinxDatetime) { -%>\n implementation(npm("@js-joda/timezone", "2.3.0"))\n <%_ } -%>\n }\n }\n}\n\nnode {\n download = true\n version = libs.versions.nodejs.get()\n}\n\n// Packs the result of the JS compilation, for consumption by the React app\nval packJsPackage by tasks.registering(NpmTask::class) {\n group = "build"\n dependsOn("jsBrowserProductionLibraryDistribution")\n\n val libraryDir = "build/dist/js/productionLibrary"\n npmCommand = listOf("pack", libraryDir, "--pack-destination", "build")\n\n inputs.dir(libraryDir)\n outputs.file("build/<%= formId %>-shared-${version}.tgz")\n}\n';
984
926
  const jsJodaTimeZoneKtEjs = 'package <%= filePackage %>\n\n// See: https://github.com/Kotlin/kotlinx-datetime?tab=readme-ov-file#note-about-time-zones-in-js\n@JsModule("@js-joda/timezone")\n@JsNonModule\nexternal object JsJodaTimeZoneModule\n\n@JsExport\nval jsJodaTz = JsJodaTimeZoneModule\n';
985
927
  const DEFAULT_JVM_TOOLCHAIN_VERSION = "17";
986
928
  const DEFAULT_NODEJS_VERSION = "22.20.0";
@@ -1406,24 +1348,17 @@ function scaffoldMain(schematic, data) {
1406
1348
  if (!file) {
1407
1349
  data.files.set(fileName, file = tsFile());
1408
1350
  }
1409
- scaffoldMainFile(schematic, {
1351
+ scaffoldMainDeclarations(schematic, {
1410
1352
  kmpModuleName: `${kebabCase(schematic.name)}-shared`,
1411
1353
  ...data,
1412
1354
  currentFile: file
1413
1355
  });
1414
1356
  }
1415
- function scaffoldMainFile(schematic, data) {
1357
+ function scaffoldMainDeclarations(schematic, data) {
1416
1358
  data.currentFile.imports.push(
1417
1359
  { moduleName: "./index.scss" },
1418
- { moduleName: "react-router-dom", name: "createHashRouter" }
1419
- );
1420
- data.currentFile.declarations.push(
1421
- `const router = createHashRouter([${scaffoldRouteObject(schematic, data)}]);`
1422
- );
1423
- data.currentFile.imports.push(
1424
- { moduleName: "react-dom/client", name: "createRoot" },
1425
1360
  { moduleName: "react", name: "StrictMode" },
1426
- { moduleName: "react-router-dom", name: "RouterProvider" },
1361
+ { moduleName: "react-dom/client", name: "createRoot" },
1427
1362
  {
1428
1363
  moduleName: data.kmpModuleName,
1429
1364
  name: `decode${schematic.name}ExternalContextsFromString`
@@ -1431,6 +1366,10 @@ function scaffoldMainFile(schematic, data) {
1431
1366
  {
1432
1367
  moduleName: `./${schematic.name}Context.ts`,
1433
1368
  name: `${schematic.name}Context`
1369
+ },
1370
+ {
1371
+ moduleName: `./${schematic.name}App.tsx`,
1372
+ name: `${schematic.name}App`
1434
1373
  }
1435
1374
  );
1436
1375
  data.currentFile.declarations.push(code`
@@ -1446,51 +1385,13 @@ function scaffoldMainFile(schematic, data) {
1446
1385
  createRoot(root).render(
1447
1386
  <StrictMode>
1448
1387
  <${schematic.name}Context.Provider value={{ externalContexts }}>
1449
- <RouterProvider router={router} />
1388
+ <${schematic.name}App />
1450
1389
  </${schematic.name}Context.Provider>
1451
1390
  </StrictMode>,
1452
1391
  );
1453
1392
  };
1454
1393
  `);
1455
1394
  }
1456
- function scaffoldRouteObject(schematic, data) {
1457
- const layout = reactAppLayout(schematic, data);
1458
- const formPages = layout.flatMap((annex) => annex.formPages);
1459
- data.currentFile.imports.push({
1460
- moduleName: `./${schematic.name}App.tsx`,
1461
- name: `${schematic.name}App`
1462
- });
1463
- for (const formPage of formPages) {
1464
- data.currentFile.imports.push({
1465
- moduleName: `./${joinPaths(
1466
- formPage.data.currentDir.slice(
1467
- data.currentDir.length + (data.currentDir.endsWith("/") ? 0 : 1)
1468
- ),
1469
- pascalCase(formPage.schematic.name)
1470
- )}.tsx`,
1471
- name: pascalCase(formPage.schematic.name)
1472
- });
1473
- }
1474
- const firstFormPage = formPages.find(
1475
- (formPage) => !formPage.schematic.nullable && !formPage.data.currentPath.split("/").includes("*")
1476
- );
1477
- const routes = [
1478
- firstFormPage && `{ path: "*", element: <${pascalCase(firstFormPage.schematic.name)} /> }`,
1479
- ...formPages.filter((formPage) => formPage !== firstFormPage).map(
1480
- (formPage) => `{ path: "${formPage.data.currentPath.split("/").map((f) => f === "*" ? ":id" : f).join(
1481
- "/"
1482
- )}", element: <${pascalCase(formPage.schematic.name)} /> }`
1483
- )
1484
- ];
1485
- return code`
1486
- {
1487
- element: <${schematic.name}App />,
1488
- children: [
1489
- ${routes.filter((r) => r).join(",\n")}
1490
- ]
1491
- }
1492
- `;
1493
- }
1494
1395
  const editorconfig = "# http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[{*.kt,*.kts,*.md}]\nindent_size = 4\nmax_line_length = 100\n\n[{*.js,*.jsx,*.ts,*.tsx,*.json,*.html,*.css,*.scss,*.toml}]\nindent_size = 2\nmax_line_length = 80";
1495
1396
  const gitignore = "# Gradle\n.gradle/\nbuild/\n!**/src/**/build/\n\n# Kotlin\n.kotlin/\n\n# Node.js\nnode_modules/\n.npmrc\n\n# Typescript\n*.tsbuildinfo\n\n# Local env files\n.env*.local\n\n# Logs\nlogs\n*.log\n\n# IDEs and editors\n.vscode/\n.idea/\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\nout/\n!**/src/**/out/\n\n# Misc\n.DS_Store\n*.pem\n";
1496
1397
  function scaffoldProjectMisc(_schematic, data) {
@@ -1532,7 +1433,7 @@ function scaffoldStyles(_schematic, data) {
1532
1433
  }
1533
1434
  const eslintConfigJs = 'import js from "@eslint/js";\nimport { defineConfig, globalIgnores } from "eslint/config";\nimport reactHooks from "eslint-plugin-react-hooks";\nimport reactRefresh from "eslint-plugin-react-refresh";\nimport globals from "globals";\nimport tseslint from "typescript-eslint";\n\nexport default defineConfig([\n globalIgnores([".gradle", "build"]),\n {\n files: ["**/*.{ts,tsx}"],\n extends: [\n js.configs.recommended,\n tseslint.configs.recommended,\n reactHooks.configs.flat.recommended,\n reactRefresh.configs.vite,\n ],\n languageOptions: {\n ecmaVersion: 2020,\n globals: globals.browser,\n },\n },\n]);\n';
1534
1435
  const indexHtmlEjs = '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="UTF-8" />\n <link rel="icon" type="image/svg+xml" href="/vite.svg" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <title><%= formTitle %></title>\n </head>\n <body>\n <div\n id="<%= formId %>-app-root"\n data-external-contexts="<%= externalContextsData %>"\n ></div>\n <script type="module" src="/src/main.tsx"><\/script>\n <script type="module">\n render<%= formClass %>App();\n <\/script>\n </body>\n</html>\n';
1535
- const packageJsonEjs = '{\n "name": "<%= formId %>-react-app",\n "private": true,\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "tsc -b && vite build",\n "lint": "eslint .",\n "preview": "vite preview"\n },\n "dependencies": {\n "@fortawesome/fontawesome-svg-core": "<%= dependencyVersions["@fortawesome/fontawesome-svg-core"] %>",\n "@fortawesome/free-regular-svg-icons": "<%= dependencyVersions["@fortawesome/free-regular-svg-icons"] %>",\n "@fortawesome/free-solid-svg-icons": "<%= dependencyVersions["@fortawesome/free-solid-svg-icons"] %>",\n "@ostack.tech/kform-react": "~<%= kFormVersion %>",\n "@ostack.tech/ui": "~<%= ostackUiVersion %>",\n "@ostack.tech/ui-kform": "~<%= ostackUiVersion %>",\n "date-fns": "<%= dependencyVersions["date-fns"] %>",\n "<%= formId %>-shared": "file:../shared/build/<%= formId %>-shared-0.0.1.tgz",\n "react": "<%= dependencyVersions.react %>",\n "react-dom": "<%= dependencyVersions["react-dom"] %>",\n "react-router": "<%= dependencyVersions["react-router"] %>",\n "react-router-dom": "<%= dependencyVersions["react-router-dom"] %>"\n },\n "devDependencies": {\n "@eslint/js": "<%= dependencyVersions["@eslint/js"] %>",\n "@types/node": "<%= dependencyVersions["@types/node"] %>",\n "@types/ostack.tech__kform": "npm:@ostack.tech/kform@~<%= kFormVersion %>",\n "@types/react": "<%= dependencyVersions["@types/react"] %>",\n "@types/react-dom": "<%= dependencyVersions["@types/react-dom"] %>",\n "@vitejs/plugin-react-swc": "<%= dependencyVersions["@vitejs/plugin-react-swc"] %>",\n "eslint": "<%= dependencyVersions.eslint %>",\n "eslint-plugin-react-hooks": "<%= dependencyVersions["eslint-plugin-react-hooks"] %>",\n "eslint-plugin-react-refresh": "<%= dependencyVersions["eslint-plugin-react-refresh"] %>",\n "globals": "<%= dependencyVersions.globals %>",\n "sass-embedded": "<%= dependencyVersions["sass-embedded"] %>",\n "typescript": "<%= dependencyVersions.typescript %>",\n "typescript-eslint": "<%= dependencyVersions["typescript-eslint"] %>",\n "vite": "<%= dependencyVersions.vite %>"\n }\n}\n';
1436
+ const packageJsonEjs = '{\n "name": "<%= formId %>-react-app",\n "private": true,\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "tsc -b && vite build",\n "lint": "eslint .",\n "preview": "vite preview"\n },\n "dependencies": {\n "@fortawesome/fontawesome-svg-core": "<%= dependencyVersions["@fortawesome/fontawesome-svg-core"] %>",\n "@fortawesome/free-regular-svg-icons": "<%= dependencyVersions["@fortawesome/free-regular-svg-icons"] %>",\n "@fortawesome/free-solid-svg-icons": "<%= dependencyVersions["@fortawesome/free-solid-svg-icons"] %>",\n "@ostack.tech/kform-react": "~<%= kFormVersion %>",\n "@ostack.tech/ui": "~<%= ostackUiVersion %>",\n "@ostack.tech/ui-kform": "~<%= ostackUiVersion %>",\n "date-fns": "<%= dependencyVersions["date-fns"] %>",\n "<%= formId %>-shared": "file:../shared/build/<%= formId %>-shared-0.0.1.tgz",\n "react": "<%= dependencyVersions.react %>",\n "react-dom": "<%= dependencyVersions["react-dom"] %>"\n },\n "devDependencies": {\n "@eslint/js": "<%= dependencyVersions["@eslint/js"] %>",\n "@types/node": "<%= dependencyVersions["@types/node"] %>",\n "@types/ostack.tech__kform": "npm:@ostack.tech/kform@~<%= kFormVersion %>",\n "@types/react": "<%= dependencyVersions["@types/react"] %>",\n "@types/react-dom": "<%= dependencyVersions["@types/react-dom"] %>",\n "@vitejs/plugin-react-swc": "<%= dependencyVersions["@vitejs/plugin-react-swc"] %>",\n "eslint": "<%= dependencyVersions.eslint %>",\n "eslint-plugin-react-hooks": "<%= dependencyVersions["eslint-plugin-react-hooks"] %>",\n "eslint-plugin-react-refresh": "<%= dependencyVersions["eslint-plugin-react-refresh"] %>",\n "globals": "<%= dependencyVersions.globals %>",\n "sass-embedded": "<%= dependencyVersions["sass-embedded"] %>",\n "typescript": "<%= dependencyVersions.typescript %>",\n "typescript-eslint": "<%= dependencyVersions["typescript-eslint"] %>",\n "vite": "<%= dependencyVersions.vite %>"\n }\n}\n';
1536
1437
  const viteSvg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>\n';
1537
1438
  const tsConfigAppJson = '{\n "compilerOptions": {\n "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",\n "target": "ES2022",\n "useDefineForClassFields": true,\n "lib": ["ES2022", "DOM", "DOM.Iterable"],\n "module": "ESNext",\n "types": ["vite/client"],\n "skipLibCheck": true,\n\n /* Bundler mode */\n "moduleResolution": "bundler",\n "allowImportingTsExtensions": true,\n "verbatimModuleSyntax": true,\n "moduleDetection": "force",\n "noEmit": true,\n "jsx": "react-jsx",\n\n /* Linting */\n "strict": true,\n "noUnusedLocals": true,\n "noUnusedParameters": true,\n "erasableSyntaxOnly": true,\n "noFallthroughCasesInSwitch": true,\n "noUncheckedSideEffectImports": true\n },\n "include": ["src"]\n}\n';
1538
1439
  const tsConfigJson = '{\n "files": [],\n "references": [\n { "path": "./tsconfig.app.json" },\n { "path": "./tsconfig.node.json" }\n ]\n}\n';
@@ -1545,9 +1446,9 @@ function scaffoldViteProject(schematic, data) {
1545
1446
  formId: kebabCase(schematic.name),
1546
1447
  formClass: schematic.name,
1547
1448
  formTitle: capitalCase(schematic.name),
1548
- ostackUiVersion: "0.3.4",
1449
+ ostackUiVersion: "0.4.0",
1549
1450
  kFormVersion: "0.31.5",
1550
- dependencyVersions: JSON.parse('{"@eslint/js":"^9.38.0","@fortawesome/fontawesome-svg-core":"^7.1.0","@fortawesome/free-brands-svg-icons":"^7.1.0","@fortawesome/free-regular-svg-icons":"^7.1.0","@fortawesome/free-solid-svg-icons":"^7.1.0","@ostack.tech/kform":"~0.31.5","@ostack.tech/kform-react":"~0.31.5","@ostack.tech/kform-scaffolder":"~0.31.5","@storybook/addon-docs":"^9.1.16","@storybook/addon-links":"^9.1.16","@storybook/react-vite":"^9.1.16","@types/node":"^22.18.13","@types/react":"^19.2.2","@types/react-dom":"^19.2.2","@vitejs/plugin-react-swc":"^4.2.0","change-case":"^5.4.4","cpy-cli":"^6.0.0","date-fns":"^4.1.0","eslint":"^9.38.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","eslint-plugin-react-hooks":"^7.0.1","eslint-plugin-react-refresh":"^0.4.24","eslint-plugin-simple-import-sort":"^12.1.1","eslint-plugin-storybook":"^9.1.16","globals":"^16.4.0","happy-dom":"^20.0.10","lint-staged":"^16.2.6","prettier":"^3.6.2","prettier-plugin-jsdoc":"^1.5.0","react":"^19.2.0","react-dom":"^19.2.0","react-router":"^7.9.5","react-router-dom":"^7.9.5","rimraf":"^6.0.1","sass-embedded":"^1.93.2","simple-git-hooks":"^2.13.1","storybook":"^9.1.16","terser":"^5.44.0","tslib":"^2.8.1","typescript":"~5.9.3","typescript-eslint":"^8.46.2","vite":"^7.1.12","vite-plugin-dts":"^4.5.4","vitest":"^4.0.5","zustand":"^5.0.8"}'),
1451
+ dependencyVersions: JSON.parse('{"@eslint/js":"^9.39.1","@fortawesome/fontawesome-svg-core":"^7.1.0","@fortawesome/free-brands-svg-icons":"^7.1.0","@fortawesome/free-regular-svg-icons":"^7.1.0","@fortawesome/free-solid-svg-icons":"^7.1.0","@ostack.tech/kform":"~0.31.5","@ostack.tech/kform-react":"~0.31.5","@ostack.tech/kform-scaffolder":"~0.31.5","@storybook/addon-docs":"^10.0.5","@storybook/addon-links":"^10.0.5","@storybook/react-vite":"^10.0.5","@types/node":"^24.10.0","@types/react":"^19.2.2","@types/react-dom":"^19.2.2","@vitejs/plugin-react-swc":"^4.2.1","change-case":"^5.4.4","cpy-cli":"^6.0.0","date-fns":"^4.1.0","eslint":"^9.39.1","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","eslint-plugin-react-hooks":"^7.0.1","eslint-plugin-react-refresh":"^0.4.24","eslint-plugin-simple-import-sort":"^12.1.1","eslint-plugin-storybook":"^10.0.5","globals":"^16.5.0","happy-dom":"^20.0.10","lint-staged":"^16.2.6","prettier":"^3.6.2","prettier-plugin-jsdoc":"^1.5.0","react":"^19.2.0","react-dom":"^19.2.0","rimraf":"^6.1.0","sass-embedded":"^1.93.2","simple-git-hooks":"^2.13.1","storybook":"^10.0.5","terser":"^5.44.1","tslib":"^2.8.1","typescript":"~5.9.3","typescript-eslint":"^8.46.3","vite":"^7.2.1","vite-plugin-dts":"^4.5.4","vitest":"^4.0.7","zustand":"^5.0.8"}'),
1551
1452
  externalContextsData: data.serializationFormat === "json" ? "{}" : `<${schematic.name}ExternalContexts />`,
1552
1453
  apiBasePath: data.apiBasePath ?? DEFAULT_API_BASE_PATH,
1553
1454
  viteConfigOutDir: data.viteConfigOutDir ?? DEFAULT_VITE_CONFIG_OUT_DIR,
@@ -1596,7 +1497,7 @@ function SchematicBuilder({
1596
1497
  configScaffolder(scaffoldAppContext, reactAppData),
1597
1498
  configScaffolder(scaffoldActions, reactAppData),
1598
1499
  configScaffolder(scaffoldApi, reactAppData),
1599
- configScaffolder(scaffoldFormPages, reactAppData)
1500
+ configScaffolder(scaffoldPageComponents, reactAppData)
1600
1501
  ],
1601
1502
  scaffoldingData = (schematic) => ({
1602
1503
  useFileBase64Serializer: true,
@@ -1622,6 +1523,7 @@ function SchematicBuilder({
1622
1523
  );
1623
1524
  }
1624
1525
  export {
1526
+ DEFAULT_ACTIVE_PATH_SEARCH_PARAM,
1625
1527
  DEFAULT_API_BASE_PATH,
1626
1528
  DEFAULT_JVM_TOOLCHAIN_VERSION,
1627
1529
  DEFAULT_LOCALE,
@@ -1658,21 +1560,21 @@ export {
1658
1560
  longSchematicKind,
1659
1561
  reactAppLayout,
1660
1562
  scaffoldActions,
1661
- scaffoldAnnexesArray,
1563
+ scaffoldAnnex,
1564
+ scaffoldAnnexes,
1662
1565
  scaffoldApi,
1663
1566
  scaffoldAppComponent,
1664
1567
  scaffoldAppContext,
1665
1568
  scaffoldExternalContexts,
1666
1569
  scaffoldField,
1667
- scaffoldFormPages,
1668
- scaffoldFormPagesByAnnexObject,
1570
+ scaffoldFormPage,
1669
1571
  scaffoldGradleProject,
1670
1572
  scaffoldGradleWrapper,
1671
1573
  scaffoldIssueMessages,
1672
1574
  scaffoldMain,
1575
+ scaffoldPageComponents,
1673
1576
  scaffoldProjectMisc,
1674
1577
  scaffoldResponses,
1675
- scaffoldRouteObject,
1676
1578
  scaffoldSerializer,
1677
1579
  scaffoldStyles,
1678
1580
  scaffoldViteProject,