zudojs-cli 2.0.0 → 2.1.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.
Files changed (249) hide show
  1. package/README.md +69 -9
  2. package/dist/src/adapters/frontend/angular.adapter.d.ts +0 -1
  3. package/dist/src/adapters/frontend/angular.adapter.js +14 -97
  4. package/dist/src/adapters/frontend/astro.adapter.d.ts +0 -1
  5. package/dist/src/adapters/frontend/astro.adapter.js +4 -64
  6. package/dist/src/adapters/frontend/fallback/fallback.helper.d.ts +41 -0
  7. package/dist/src/adapters/frontend/fallback/fallback.helper.js +57 -0
  8. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.d.ts +18 -0
  9. package/dist/src/adapters/frontend/fallback/framework/angular/angular.fallback.js +107 -0
  10. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.d.ts +23 -0
  11. package/dist/src/adapters/frontend/fallback/framework/angular/angularWorkspace.fallback.js +125 -0
  12. package/dist/src/adapters/frontend/fallback/framework/angular/index.d.ts +8 -0
  13. package/dist/src/adapters/frontend/fallback/framework/angular/index.js +8 -0
  14. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.d.ts +14 -0
  15. package/dist/src/adapters/frontend/fallback/framework/astro.fallback.js +76 -0
  16. package/dist/src/adapters/frontend/fallback/framework/index.d.ts +13 -0
  17. package/dist/src/adapters/frontend/fallback/framework/index.js +13 -0
  18. package/dist/src/adapters/frontend/fallback/framework/next.fallback.d.ts +13 -0
  19. package/dist/src/adapters/frontend/fallback/framework/next.fallback.js +105 -0
  20. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.d.ts +14 -0
  21. package/dist/src/adapters/frontend/fallback/framework/nuxt.fallback.js +56 -0
  22. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.d.ts +15 -0
  23. package/dist/src/adapters/frontend/fallback/framework/sveltekit.fallback.js +94 -0
  24. package/dist/src/adapters/frontend/fallback/index.d.ts +12 -0
  25. package/dist/src/adapters/frontend/fallback/index.js +12 -0
  26. package/dist/src/adapters/frontend/fallback/vite/index.d.ts +11 -0
  27. package/dist/src/adapters/frontend/fallback/vite/index.js +11 -0
  28. package/dist/src/adapters/frontend/fallback/vite/react.fallback.d.ts +13 -0
  29. package/dist/src/adapters/frontend/fallback/vite/react.fallback.js +77 -0
  30. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.d.ts +14 -0
  31. package/dist/src/adapters/frontend/fallback/vite/svelte.fallback.js +95 -0
  32. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.d.ts +13 -0
  33. package/dist/src/adapters/frontend/fallback/vite/vanilla.fallback.js +71 -0
  34. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.d.ts +14 -0
  35. package/dist/src/adapters/frontend/fallback/vite/vue.fallback.js +78 -0
  36. package/dist/src/adapters/frontend/next.adapter.d.ts +0 -1
  37. package/dist/src/adapters/frontend/next.adapter.js +16 -95
  38. package/dist/src/adapters/frontend/nuxt.adapter.d.ts +1 -2
  39. package/dist/src/adapters/frontend/nuxt.adapter.js +32 -43
  40. package/dist/src/adapters/frontend/react-native.adapter.js +1 -1
  41. package/dist/src/adapters/frontend/react.adapter.d.ts +0 -1
  42. package/dist/src/adapters/frontend/react.adapter.js +7 -89
  43. package/dist/src/adapters/frontend/svelte.adapter.d.ts +0 -1
  44. package/dist/src/adapters/frontend/svelte.adapter.js +4 -86
  45. package/dist/src/adapters/frontend/sveltekit.adapter.d.ts +0 -1
  46. package/dist/src/adapters/frontend/sveltekit.adapter.js +4 -57
  47. package/dist/src/adapters/frontend/vanilla.adapter.d.ts +0 -1
  48. package/dist/src/adapters/frontend/vanilla.adapter.js +4 -47
  49. package/dist/src/adapters/frontend/vue.adapter.d.ts +0 -1
  50. package/dist/src/adapters/frontend/vue.adapter.js +4 -76
  51. package/dist/src/bin/bin.menu.d.ts +22 -0
  52. package/dist/src/bin/bin.menu.js +40 -0
  53. package/dist/src/bin/bin.runner.d.ts +38 -0
  54. package/dist/src/bin/bin.runner.js +51 -0
  55. package/dist/src/bin/index.d.ts +9 -0
  56. package/dist/src/bin/index.js +9 -0
  57. package/dist/src/bin/zudojs.js +15 -4
  58. package/dist/src/cliApplication/cliApplication.core.d.ts +4 -2
  59. package/dist/src/cliApplication/cliApplication.core.js +48 -36
  60. package/dist/src/cliApplication/cliApplication.logger.d.ts +5 -0
  61. package/dist/src/cliApplication/cliApplication.logger.js +5 -3
  62. package/dist/src/cliApplication/invocation/index.d.ts +12 -0
  63. package/dist/src/cliApplication/invocation/index.js +12 -0
  64. package/dist/src/cliApplication/invocation/invocation.hint.d.ts +30 -0
  65. package/dist/src/cliApplication/invocation/invocation.hint.js +74 -0
  66. package/dist/src/cliApplication/invocation/invocation.locate.d.ts +34 -0
  67. package/dist/src/cliApplication/invocation/invocation.locate.js +53 -0
  68. package/dist/src/cliApplication/invocation/invocation.name.d.ts +37 -0
  69. package/dist/src/cliApplication/invocation/invocation.name.js +69 -0
  70. package/dist/src/cliApplication/invocation/invocation.sanitize.d.ts +19 -0
  71. package/dist/src/cliApplication/invocation/invocation.sanitize.js +32 -0
  72. package/dist/src/cliParser/cliParser.core.js +3 -1
  73. package/dist/src/cliParser/cliParser.helper.d.ts +7 -0
  74. package/dist/src/cliParser/cliParser.helper.js +19 -2
  75. package/dist/src/cliParser/cliParser.longOption.js +2 -2
  76. package/dist/src/commands/add.command.d.ts +7 -3
  77. package/dist/src/commands/add.command.js +96 -57
  78. package/dist/src/commands/create.command.js +29 -42
  79. package/dist/src/commands/doctor.command.d.ts +2 -1
  80. package/dist/src/commands/doctor.command.js +52 -4
  81. package/dist/src/commands/generate.command.js +31 -16
  82. package/dist/src/commands/info.command.js +11 -1
  83. package/dist/src/constants/index.d.ts +21 -3
  84. package/dist/src/constants/index.js +40 -5
  85. package/dist/src/constants/zudojsVersions.generated.d.ts +9 -0
  86. package/dist/src/constants/zudojsVersions.generated.js +48 -0
  87. package/dist/src/constants/zudojsVersions.helper.d.ts +21 -0
  88. package/dist/src/constants/zudojsVersions.helper.js +29 -0
  89. package/dist/src/generators/frontend/frontendGenerator.core.js +9 -10
  90. package/dist/src/generators/frontend/frontendPipeline.d.ts +6 -1
  91. package/dist/src/generators/frontend/frontendPipeline.js +9 -1
  92. package/dist/src/generators/fullstack/fullstackComposer.core.js +7 -1
  93. package/dist/src/generators/index.d.ts +1 -2
  94. package/dist/src/generators/index.js +1 -2
  95. package/dist/src/generators/infrastructure/infrastructure.generator.js +13 -5
  96. package/dist/src/generators/integration/integrationGenerator.core.d.ts +2 -0
  97. package/dist/src/generators/integration/integrationGenerator.core.js +31 -2
  98. package/dist/src/generators/module/module.generator.js +36 -4
  99. package/dist/src/generators/resource/index.d.ts +8 -0
  100. package/dist/src/generators/resource/index.js +8 -0
  101. package/dist/src/generators/resource/resource.generator.d.ts +27 -0
  102. package/dist/src/generators/resource/resource.generator.js +87 -0
  103. package/dist/src/generators/resource/resource.layout.d.ts +26 -0
  104. package/dist/src/generators/resource/resource.layout.js +46 -0
  105. package/dist/src/generators/resource/resource.plan.d.ts +27 -0
  106. package/dist/src/generators/resource/resource.plan.js +46 -0
  107. package/dist/src/generators/resource/resource.run.d.ts +25 -0
  108. package/dist/src/generators/resource/resource.run.js +52 -0
  109. package/dist/src/prompts/cancel.prompt.d.ts +5 -1
  110. package/dist/src/prompts/cancel.prompt.js +4 -0
  111. package/dist/src/prompts/menu/index.d.ts +13 -0
  112. package/dist/src/prompts/menu/index.js +13 -0
  113. package/dist/src/prompts/menu/menu.adapter.d.ts +16 -0
  114. package/dist/src/prompts/menu/menu.adapter.js +56 -0
  115. package/dist/src/prompts/menu/menu.constant.d.ts +20 -0
  116. package/dist/src/prompts/menu/menu.constant.js +29 -0
  117. package/dist/src/prompts/menu/menu.flow.d.ts +18 -0
  118. package/dist/src/prompts/menu/menu.flow.js +82 -0
  119. package/dist/src/prompts/menu/menu.select.d.ts +26 -0
  120. package/dist/src/prompts/menu/menu.select.js +118 -0
  121. package/dist/src/prompts/menu/menu.type.d.ts +66 -0
  122. package/dist/src/prompts/menu/menu.type.js +8 -0
  123. package/dist/src/recipes/apply/index.d.ts +9 -0
  124. package/dist/src/recipes/apply/index.js +9 -0
  125. package/dist/src/recipes/apply/recipeApply.app.d.ts +20 -0
  126. package/dist/src/recipes/apply/recipeApply.app.js +79 -0
  127. package/dist/src/recipes/apply/recipeApply.context.d.ts +14 -0
  128. package/dist/src/recipes/apply/recipeApply.context.js +76 -0
  129. package/dist/src/recipes/apply/recipeApply.files.d.ts +39 -0
  130. package/dist/src/recipes/apply/recipeApply.files.js +84 -0
  131. package/dist/src/recipes/apply/recipeApply.project.d.ts +11 -0
  132. package/dist/src/recipes/apply/recipeApply.project.js +50 -0
  133. package/dist/src/recipes/apply/recipeApply.report.d.ts +20 -0
  134. package/dist/src/recipes/apply/recipeApply.report.js +51 -0
  135. package/dist/src/recipes/catalog/database/database.files.d.ts +15 -0
  136. package/dist/src/recipes/catalog/database/database.files.js +98 -0
  137. package/dist/src/recipes/catalog/database/database.recipe.d.ts +9 -0
  138. package/dist/src/recipes/catalog/database/database.recipe.js +50 -0
  139. package/dist/src/recipes/catalog/database/index.d.ts +6 -0
  140. package/dist/src/recipes/catalog/database/index.js +6 -0
  141. package/dist/src/recipes/catalog/email.recipe.d.ts +9 -0
  142. package/dist/src/recipes/catalog/email.recipe.js +122 -0
  143. package/dist/src/recipes/catalog/index.d.ts +11 -0
  144. package/dist/src/recipes/catalog/index.js +11 -0
  145. package/dist/src/recipes/catalog/openapi.recipe.d.ts +8 -0
  146. package/dist/src/recipes/catalog/openapi.recipe.js +25 -0
  147. package/dist/src/recipes/catalog/platform/cache.recipe.d.ts +7 -0
  148. package/dist/src/recipes/catalog/platform/cache.recipe.js +55 -0
  149. package/dist/src/recipes/catalog/platform/index.d.ts +9 -0
  150. package/dist/src/recipes/catalog/platform/index.js +9 -0
  151. package/dist/src/recipes/catalog/platform/messaging.recipe.d.ts +7 -0
  152. package/dist/src/recipes/catalog/platform/messaging.recipe.js +46 -0
  153. package/dist/src/recipes/catalog/platform/observability.recipe.d.ts +8 -0
  154. package/dist/src/recipes/catalog/platform/observability.recipe.js +49 -0
  155. package/dist/src/recipes/catalog/platform/queue.recipe.d.ts +7 -0
  156. package/dist/src/recipes/catalog/platform/queue.recipe.js +61 -0
  157. package/dist/src/recipes/catalog/platform/scheduler.recipe.d.ts +7 -0
  158. package/dist/src/recipes/catalog/platform/scheduler.recipe.js +56 -0
  159. package/dist/src/recipes/catalog/redis.recipe.d.ts +8 -0
  160. package/dist/src/recipes/catalog/redis.recipe.js +79 -0
  161. package/dist/src/recipes/catalog/storage.recipe.d.ts +7 -0
  162. package/dist/src/recipes/catalog/storage.recipe.js +49 -0
  163. package/dist/src/recipes/catalog/websockets.recipe.d.ts +10 -0
  164. package/dist/src/recipes/catalog/websockets.recipe.js +84 -0
  165. package/dist/src/recipes/docker/docker.compose.d.ts +19 -0
  166. package/dist/src/recipes/docker/docker.compose.js +90 -0
  167. package/dist/src/recipes/docker/docker.recipe.d.ts +10 -0
  168. package/dist/src/recipes/docker/docker.recipe.js +51 -0
  169. package/dist/src/recipes/docker/docker.refresh.d.ts +13 -0
  170. package/dist/src/recipes/docker/docker.refresh.js +37 -0
  171. package/dist/src/recipes/docker/index.d.ts +7 -0
  172. package/dist/src/recipes/docker/index.js +7 -0
  173. package/dist/src/recipes/index.d.ts +8 -0
  174. package/dist/src/recipes/index.js +8 -0
  175. package/dist/src/recipes/recipe.registry.d.ts +17 -0
  176. package/dist/src/recipes/recipe.registry.js +46 -0
  177. package/dist/src/recipes/recipe.type.d.ts +93 -0
  178. package/dist/src/recipes/recipe.type.js +12 -0
  179. package/dist/src/resolvers/dependency/dependencyVersions.constant.d.ts +100 -5
  180. package/dist/src/resolvers/dependency/dependencyVersions.constant.js +103 -34
  181. package/dist/src/resolvers/dependency/index.d.ts +1 -1
  182. package/dist/src/resolvers/dependency/index.js +1 -1
  183. package/dist/src/resolvers/project.resolver.d.ts +14 -1
  184. package/dist/src/resolvers/project.resolver.js +11 -3
  185. package/dist/src/scaffolders/scaffolder.helper.js +12 -1
  186. package/dist/src/templates/backendApp/backendApp.core.d.ts +45 -0
  187. package/dist/src/templates/backendApp/backendApp.core.js +66 -0
  188. package/dist/src/templates/backendApp/backendApp.modules.d.ts +26 -0
  189. package/dist/src/templates/backendApp/backendApp.modules.js +25 -0
  190. package/dist/src/templates/backendApp/backendApp.project.d.ts +26 -0
  191. package/dist/src/templates/backendApp/backendApp.project.js +87 -0
  192. package/dist/src/templates/backendApp/index.d.ts +8 -0
  193. package/dist/src/templates/backendApp/index.js +8 -0
  194. package/dist/src/templates/backendApp/parts/config.template.d.ts +28 -0
  195. package/dist/src/templates/backendApp/parts/config.template.js +118 -0
  196. package/dist/src/templates/backendApp/parts/httpUtils.template.d.ts +10 -0
  197. package/dist/src/templates/backendApp/parts/httpUtils.template.js +103 -0
  198. package/dist/src/templates/backendApp/parts/index.d.ts +9 -0
  199. package/dist/src/templates/backendApp/parts/index.js +9 -0
  200. package/dist/src/templates/backendApp/parts/integrations.template.d.ts +16 -0
  201. package/dist/src/templates/backendApp/parts/integrations.template.js +115 -0
  202. package/dist/src/templates/backendApp/parts/wiring.template.d.ts +26 -0
  203. package/dist/src/templates/backendApp/parts/wiring.template.js +111 -0
  204. package/dist/src/templates/microservice/microservice.template.d.ts +4 -1
  205. package/dist/src/templates/microservice/microservice.template.js +74 -90
  206. package/dist/src/templates/modular-monolith/modularMonolith.template.js +25 -77
  207. package/dist/src/templates/monolith/monolith.template.d.ts +11 -26
  208. package/dist/src/templates/monolith/monolith.template.js +49 -146
  209. package/dist/src/templates/resource/index.d.ts +9 -0
  210. package/dist/src/templates/resource/index.js +9 -0
  211. package/dist/src/templates/resource/layers/index.d.ts +9 -0
  212. package/dist/src/templates/resource/layers/index.js +9 -0
  213. package/dist/src/templates/resource/layers/resourceData.template.d.ts +9 -0
  214. package/dist/src/templates/resource/layers/resourceData.template.js +108 -0
  215. package/dist/src/templates/resource/layers/resourcePrisma.template.d.ts +15 -0
  216. package/dist/src/templates/resource/layers/resourcePrisma.template.js +89 -0
  217. package/dist/src/templates/resource/layers/resourceRoutes.template.d.ts +13 -0
  218. package/dist/src/templates/resource/layers/resourceRoutes.template.js +128 -0
  219. package/dist/src/templates/resource/layers/resourceService.template.d.ts +13 -0
  220. package/dist/src/templates/resource/layers/resourceService.template.js +95 -0
  221. package/dist/src/templates/resource/resource.files.d.ts +33 -0
  222. package/dist/src/templates/resource/resource.files.js +99 -0
  223. package/dist/src/templates/resource/resource.names.d.ts +38 -0
  224. package/dist/src/templates/resource/resource.names.js +60 -0
  225. package/dist/src/templates/resource/resource.type.d.ts +19 -0
  226. package/dist/src/templates/resource/resource.type.js +15 -0
  227. package/dist/src/templates/shared/appRuntime.template.js +28 -11
  228. package/dist/src/templates/shared/capability.template.d.ts +13 -4
  229. package/dist/src/templates/shared/capability.template.js +21 -5
  230. package/dist/src/templates/shared/dockerfile.template.d.ts +12 -4
  231. package/dist/src/templates/shared/dockerfile.template.js +68 -29
  232. package/dist/src/templates/shared/pnpm.template.d.ts +8 -2
  233. package/dist/src/templates/shared/pnpm.template.js +9 -1
  234. package/dist/src/templates/shared/server.template.d.ts +23 -22
  235. package/dist/src/templates/shared/server.template.js +79 -53
  236. package/dist/src/types/scaffold.type.d.ts +6 -0
  237. package/dist/src/types/scaffold.type.js +2 -1
  238. package/dist/src/utils/utils.name.js +6 -0
  239. package/dist/src/utils/utils.writeGuard.d.ts +5 -0
  240. package/dist/src/utils/utils.writeGuard.js +23 -0
  241. package/dist/src/wiring/index.d.ts +10 -0
  242. package/dist/src/wiring/index.js +10 -0
  243. package/dist/src/wiring/wiring.dependencies.d.ts +29 -0
  244. package/dist/src/wiring/wiring.dependencies.js +83 -0
  245. package/dist/src/wiring/wiring.edits.d.ts +51 -0
  246. package/dist/src/wiring/wiring.edits.js +90 -0
  247. package/dist/src/wiring/wiring.markers.d.ts +53 -0
  248. package/dist/src/wiring/wiring.markers.js +87 -0
  249. package/package.json +20 -15
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @module generators/integration
5
5
  */
6
- import { existsSync } from "node:fs";
6
+ import { existsSync, readFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { mergeBarrelExport, writeFileTree, } from "../../utils/utils.fileSystem.js";
9
9
  import { renderDatabaseEnv } from "../../adapters/databases/databaseAdapter.resolver.js";
@@ -33,6 +33,18 @@ export class IntegrationGenerator {
33
33
  const configDir = `${this.getBackendPath(context)}src/configs`;
34
34
  files[`${configDir}/cors.ts`] = this.generateCorsConfig(context);
35
35
  files[`${configDir}/index.ts`] = mergeBarrelExport(context.projectPath, `${configDir}/index.ts`, `export { corsConfig } from "./cors.js";`);
36
+ // The generated server enforces CORS_ORIGINS (empty: no browser
37
+ // origin allowed), so the frontend's dev origin must be listed there or
38
+ // every cross-origin call from it is refused.
39
+ const backendEnv = `${this.getBackendPath(context)}.env.example`;
40
+ const backendEnvPath = join(context.projectPath, backendEnv);
41
+ if (existsSync(backendEnvPath)) {
42
+ const origins = this.frontendOrigins(context).join(",");
43
+ const current = readFileSync(backendEnvPath, "utf-8");
44
+ if (/^CORS_ORIGINS=$/m.test(current)) {
45
+ files[backendEnv] = current.replace(/^CORS_ORIGINS=$/m, `CORS_ORIGINS=${origins}`);
46
+ }
47
+ }
36
48
  // Development proxy configuration (belongs to the frontend app). The
37
49
  // frontend adapter has already written a vite.config.ts carrying the
38
50
  // framework plugin and path aliases; overwriting it with this minimal
@@ -59,10 +71,22 @@ export class IntegrationGenerator {
59
71
  // written anywhere, so nothing downstream could agree with the CORS
60
72
  // origins generated from the same value.
61
73
  `FRONTEND_PORT=${frontendPort}`,
74
+ `CORS_ORIGINS=${this.frontendOrigins(context).join(",")}`,
62
75
  `VITE_API_URL=http://localhost:${backendPort}`,
63
76
  "",
64
77
  "# Database",
65
- renderDatabaseEnv(context.project.backend?.database, "mydb"),
78
+ // The database docker-compose.yml creates (it was "mydb").
79
+ renderDatabaseEnv(context.project.backend?.database, context.project.name),
80
+ // Empty, never a placeholder such as "change-me": docker-compose.yml
81
+ // refuses to start until it is set. Hex keeps it valid in a URL.
82
+ ...(context.project.backend?.database === "sqlite"
83
+ ? []
84
+ : [
85
+ "# Password for the docker-compose.yml database; generate with: openssl rand -hex 32",
86
+ context.project.backend?.database === "mysql"
87
+ ? "MYSQL_ROOT_PASSWORD="
88
+ : "POSTGRES_PASSWORD=",
89
+ ]),
66
90
  "",
67
91
  ];
68
92
  if (context.project.frontend?.framework === "next") {
@@ -167,6 +191,11 @@ export const api = {
167
191
  }
168
192
  return "apps/web/src/services/api-client.ts";
169
193
  }
194
+ /** Origins the frontend dev server is reached on. */
195
+ frontendOrigins(context) {
196
+ const port = context.frontendPort ?? 5173;
197
+ return [`http://localhost:${port}`, `http://127.0.0.1:${port}`];
198
+ }
170
199
  generateCorsConfig(context) {
171
200
  const frontendPort = context.frontendPort ?? 5173;
172
201
  return `/**
@@ -7,10 +7,14 @@
7
7
  */
8
8
  import { basename, dirname } from "node:path";
9
9
  import { mergeBarrelExport, writeFileTree } from "../../utils/utils.fileSystem.js";
10
- import { CLIGenerationError } from "../../errors/index.js";
10
+ import { CLIGenerationError, CLIValidationError } from "../../errors/index.js";
11
11
  import { assertGeneratableName, toPascalCase } from "../../utils/utils.name.js";
12
12
  import { moduleSpec, renderModuleFile } from "../../templates/shared/appRuntime.template.js";
13
13
  import { registerModuleInApp } from "./module.registration.js";
14
+ import { moduleRoutesWiring, renderModuleRoutesIndex, } from "../../templates/backendApp/index.js";
15
+ import { MARKERS, applyMarkerEdits, conflictingImport } from "../../wiring/index.js";
16
+ import { existsSync } from "node:fs";
17
+ import { join } from "node:path";
14
18
  export async function generateModule(options, cwd) {
15
19
  const basePath = options.basePath ?? "modules";
16
20
  const name = assertGeneratableName(options.name, "module name");
@@ -34,8 +38,21 @@ export class ${namePascal}Feature {
34
38
  files[`${basePath}/${name}/features/index.ts`] = `export { ${namePascal}Feature } from "./${featureName}.js";
35
39
  `;
36
40
  }
41
+ // A module gets its own routes index, registered with the app's
42
+ // src/routes/index.ts, when the app has one (every app `create` writes).
43
+ const appSrc = dirname(basePath);
44
+ const appRoutes = `${appSrc}/routes/index.ts`;
45
+ const routes = moduleRoutesWiring(name);
46
+ const hasAppRoutes = existsSync(join(cwd, appRoutes));
47
+ const clash = hasAppRoutes ? conflictingImport(cwd, appRoutes, routes.importLine) : undefined;
48
+ if (clash !== undefined) {
49
+ throw new CLIValidationError(`${appRoutes} already imports ${routes.functionName} (${clash.trim()}), so module "${name}" cannot register its routes. Choose another name.`);
50
+ }
51
+ if (hasAppRoutes) {
52
+ files[`${basePath}/${name}/routes/index.ts`] = renderModuleRoutesIndex(routes.functionName, "../../../container.js");
53
+ }
37
54
  if (options.dryRun) {
38
- return Object.keys(files);
55
+ return hasAppRoutes ? [...Object.keys(files), appRoutes] : Object.keys(files);
39
56
  }
40
57
  try {
41
58
  await writeFileTree(cwd, files);
@@ -43,9 +60,24 @@ export class ${namePascal}Feature {
43
60
  catch (error) {
44
61
  throw new CLIGenerationError(`Failed to generate module: ${name}`, error);
45
62
  }
63
+ const routeEdits = hasAppRoutes
64
+ ? await applyMarkerEdits(cwd, [
65
+ { file: appRoutes, marker: MARKERS.routeImports, line: routes.importLine },
66
+ { file: appRoutes, marker: MARKERS.routes, line: routes.entryLine },
67
+ ])
68
+ : undefined;
46
69
  const appPath = `${dirname(basePath)}/app.ts`;
47
70
  const registration = registerModuleInApp(cwd, appPath, spec.className, `./${basename(basePath)}/index.js`);
48
- options.onRegistered?.(registration);
49
- return registration.registered ? [...Object.keys(files), appPath] : Object.keys(files);
71
+ options.onRegistered?.(routeEdits && routeEdits.manualSteps.length > 0
72
+ ? {
73
+ registered: registration.registered,
74
+ manualSteps: [...registration.manualSteps, ...routeEdits.manualSteps],
75
+ }
76
+ : registration);
77
+ return [
78
+ ...Object.keys(files),
79
+ ...(registration.registered ? [appPath] : []),
80
+ ...(routeEdits?.edited ?? []),
81
+ ];
50
82
  }
51
83
  //# sourceMappingURL=module.generator.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * zudojs-cli — Resource generator: `generate resource|route|controller|repository|dto`.
3
+ */
4
+ export * from "./resource.plan.js";
5
+ export * from "./resource.generator.js";
6
+ export * from "./resource.layout.js";
7
+ export * from "./resource.run.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * zudojs-cli — Resource generator: `generate resource|route|controller|repository|dto`.
3
+ */
4
+ export * from "./resource.plan.js";
5
+ export * from "./resource.generator.js";
6
+ export * from "./resource.layout.js";
7
+ export * from "./resource.run.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * zudojs-cli — Resource generator.
3
+ *
4
+ * Writes the files of a resource-family schematic (see `resource.plan.ts`)
5
+ * and wires them: the routes are registered between the markers of the
6
+ * nearest `routes/index.ts`, and the controller is constructed once, in the
7
+ * app's `src/container.ts`. Running it twice for the same name fails with
8
+ * the files that already exist; it never duplicates a registration.
9
+ */
10
+ import { type ResourceLayout } from "../../templates/resource/index.js";
11
+ import { type ResourceSchematic } from "./resource.plan.js";
12
+ /** Options for {@link generateResource}. */
13
+ export interface GenerateResourceOptions {
14
+ readonly name: string;
15
+ readonly schematic: ResourceSchematic;
16
+ readonly layout: ResourceLayout;
17
+ readonly dryRun?: boolean;
18
+ /** Overwrite the schematic's own files when they exist. */
19
+ readonly force?: boolean;
20
+ }
21
+ /** What {@link generateResource} wrote and what is left to do by hand. */
22
+ export interface ResourceGenerationResult {
23
+ readonly files: readonly string[];
24
+ readonly manualSteps: readonly string[];
25
+ }
26
+ export declare function generateResource(options: GenerateResourceOptions, cwd: string): Promise<ResourceGenerationResult>;
27
+ //# sourceMappingURL=resource.generator.d.ts.map
@@ -0,0 +1,87 @@
1
+ /**
2
+ * zudojs-cli — Resource generator.
3
+ *
4
+ * Writes the files of a resource-family schematic (see `resource.plan.ts`)
5
+ * and wires them: the routes are registered between the markers of the
6
+ * nearest `routes/index.ts`, and the controller is constructed once, in the
7
+ * app's `src/container.ts`. Running it twice for the same name fails with
8
+ * the files that already exist; it never duplicates a registration.
9
+ */
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { CLIValidationError } from "../../errors/index.js";
13
+ import { renderHttpUtils } from "../../templates/backendApp/index.js";
14
+ import { hasPrismaModel, joinPath, renderPrismaModel, renderResourceLayers, resourceLayerPaths, resourceNames, resourceWiring, } from "../../templates/resource/index.js";
15
+ import { writeFileTree } from "../../utils/utils.fileSystem.js";
16
+ import { MARKERS, applyMarkerEdits, conflictingImport, } from "../../wiring/index.js";
17
+ import { planResource } from "./resource.plan.js";
18
+ export async function generateResource(options, cwd) {
19
+ const names = resourceNames(options.name);
20
+ const { layout } = options;
21
+ const plan = planResource(options.schematic);
22
+ const exists = (path) => existsSync(join(cwd, path));
23
+ const primaryPaths = plan.primary.flatMap((layer) => resourceLayerPaths(names, layout, layer));
24
+ const taken = primaryPaths.filter(exists);
25
+ if (taken.length > 0 && options.force !== true) {
26
+ throw new CLIValidationError(`${options.schematic} "${names.slug}" already exists:\n${taken
27
+ .map((path) => ` - ${path}`)
28
+ .join("\n")}\nChoose another name, or re-run with --force to regenerate these files.`);
29
+ }
30
+ const wiring = resourceWiring(names, layout);
31
+ if (plan.register && exists(wiring.container)) {
32
+ const container = readFileSync(join(cwd, wiring.container), "utf-8");
33
+ if (container.includes(`${wiring.containerKey}:`) && taken.length === 0) {
34
+ throw new CLIValidationError(`${wiring.container} already constructs "${wiring.containerKey}" (a resource named "${names.slug}" exists elsewhere in this app). Choose another name.`);
35
+ }
36
+ }
37
+ const clash = plan.register
38
+ ? conflictingImport(cwd, wiring.routesIndex, wiring.routeImport)
39
+ : undefined;
40
+ if (clash !== undefined) {
41
+ throw new CLIValidationError(`${wiring.routesIndex} already imports the routes function ${options.schematic} "${names.slug}" would register (${clash.trim()}). Choose another name.`);
42
+ }
43
+ const missingRequired = plan.required.filter((layer) => resourceLayerPaths(names, layout, layer).some((path) => !exists(path)));
44
+ const files = renderResourceLayers(names, layout, [
45
+ ...missingRequired,
46
+ ...plan.primary,
47
+ ]);
48
+ // Older projects have no src/utils/http.ts, which controllers import.
49
+ const httpUtils = joinPath(layout.appSrc, "utils", "http.ts");
50
+ if ((plan.primary.includes("controller") || missingRequired.includes("controller")) &&
51
+ !exists(httpUtils)) {
52
+ files[httpUtils] = renderHttpUtils();
53
+ }
54
+ const writesRepository = plan.primary.includes("repository") || missingRequired.includes("repository");
55
+ const schemaPath = joinPath(layout.appRoot, "prisma", "schema.prisma");
56
+ if (layout.prisma && writesRepository && exists(schemaPath)) {
57
+ const schema = readFileSync(join(cwd, schemaPath), "utf-8");
58
+ if (!hasPrismaModel(schema, names.entity)) {
59
+ files[schemaPath] = `${schema.replace(/\n*$/, "\n")}${renderPrismaModel(names)}`;
60
+ }
61
+ }
62
+ const edits = plan.register
63
+ ? [
64
+ { file: wiring.routesIndex, marker: MARKERS.routeImports, line: wiring.routeImport },
65
+ { file: wiring.routesIndex, marker: MARKERS.routes, line: wiring.routeEntry },
66
+ ...wiring.containerImports.map((line) => ({
67
+ file: wiring.container,
68
+ marker: MARKERS.containerImports,
69
+ line,
70
+ })),
71
+ { file: wiring.container, marker: MARKERS.container, line: wiring.containerEntry },
72
+ ]
73
+ : [];
74
+ if (!options.dryRun) {
75
+ await writeFileTree(cwd, files);
76
+ }
77
+ const outcome = await applyMarkerEdits(cwd, edits, options.dryRun === true);
78
+ const manualSteps = [...outcome.manualSteps];
79
+ if (layout.prisma && writesRepository) {
80
+ manualSteps.push(`Run "prisma migrate dev --name add-${names.slug}" (the ${names.entity} model was added to ${schemaPath}).`);
81
+ }
82
+ return {
83
+ files: [...Object.keys(files), ...outcome.edited],
84
+ manualSteps,
85
+ };
86
+ }
87
+ //# sourceMappingURL=resource.generator.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * zudojs-cli — Where `generate` puts a resource in each architecture.
3
+ *
4
+ * - monolith / modular monolith: the app is the project root (`apps/api`
5
+ * in a fullstack workspace); `--module <name>` places the resource in
6
+ * `src/modules/<name>/` and registers it in that module's routes index.
7
+ * - microservice: the gateway app, or `apps/services/<name>` with
8
+ * `--service <name>`; `--module` works inside that app the same way.
9
+ */
10
+ import { type ResourceLayout } from "../../templates/resource/index.js";
11
+ /** Inputs of {@link resolveResourceLayout}. */
12
+ export interface ResourceLayoutInput {
13
+ readonly cwd: string;
14
+ readonly architecture: string | undefined;
15
+ /** `""` or `"apps/api"`: where a non-microservice backend lives. */
16
+ readonly backendRoot: string;
17
+ readonly service?: string;
18
+ readonly module?: string;
19
+ }
20
+ /**
21
+ * Resolves the {@link ResourceLayout} for a generate call.
22
+ *
23
+ * @throws {CLIValidationError} When the selected service or module does not exist.
24
+ */
25
+ export declare function resolveResourceLayout(input: ResourceLayoutInput): ResourceLayout;
26
+ //# sourceMappingURL=resource.layout.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * zudojs-cli — Where `generate` puts a resource in each architecture.
3
+ *
4
+ * - monolith / modular monolith: the app is the project root (`apps/api`
5
+ * in a fullstack workspace); `--module <name>` places the resource in
6
+ * `src/modules/<name>/` and registers it in that module's routes index.
7
+ * - microservice: the gateway app, or `apps/services/<name>` with
8
+ * `--service <name>`; `--module` works inside that app the same way.
9
+ */
10
+ import { existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { CLIValidationError } from "../../errors/index.js";
13
+ import { joinPath } from "../../templates/resource/index.js";
14
+ /**
15
+ * Resolves the {@link ResourceLayout} for a generate call.
16
+ *
17
+ * @throws {CLIValidationError} When the selected service or module does not exist.
18
+ */
19
+ export function resolveResourceLayout(input) {
20
+ const { cwd } = input;
21
+ let appRoot = input.backendRoot;
22
+ if (input.architecture === "microservice") {
23
+ appRoot =
24
+ input.service === undefined || input.service === "gateway"
25
+ ? "apps/gateway"
26
+ : `apps/services/${input.service}`;
27
+ if (!existsSync(join(cwd, appRoot, "package.json"))) {
28
+ throw new CLIValidationError(`No app at ${appRoot}. Pass --service with an existing service (or omit it for the gateway).`);
29
+ }
30
+ }
31
+ const appSrc = joinPath(appRoot, "src");
32
+ let base = appSrc;
33
+ if (input.module !== undefined) {
34
+ base = joinPath(appSrc, "modules", input.module);
35
+ if (!existsSync(join(cwd, base))) {
36
+ throw new CLIValidationError(`Module "${input.module}" does not exist at ${base}. Create it first: zudojs generate module ${input.module}`);
37
+ }
38
+ }
39
+ return {
40
+ base,
41
+ appSrc,
42
+ appRoot,
43
+ prisma: existsSync(join(cwd, appRoot, "prisma", "schema.prisma")),
44
+ };
45
+ }
46
+ //# sourceMappingURL=resource.layout.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * zudojs-cli — Which files a resource-family schematic writes.
3
+ *
4
+ * `resource`, `route`, `controller`, `repository` and `dto` all write part
5
+ * of the same DTO → repository → service → controller → routes chain. Each
6
+ * writes its own layer ("primary") and any lower layer that does not exist
7
+ * yet ("required"), so whatever it writes compiles on its own:
8
+ * `generate controller users` also writes the users service, repository
9
+ * and DTO when they are missing, and never touches them when they exist.
10
+ */
11
+ import type { ResourceLayer } from "../../templates/resource/index.js";
12
+ /** Schematics served by the resource generator. */
13
+ export declare const RESOURCE_SCHEMATICS: readonly ["resource", "route", "controller", "repository", "dto"];
14
+ /** A schematic served by the resource generator. */
15
+ export type ResourceSchematic = (typeof RESOURCE_SCHEMATICS)[number];
16
+ /** Whether `schematic` is served by the resource generator. */
17
+ export declare function isResourceSchematic(schematic: string): schematic is ResourceSchematic;
18
+ /** The layers a schematic owns, and the ones it needs underneath. */
19
+ export interface ResourcePlan {
20
+ readonly primary: readonly ResourceLayer[];
21
+ readonly required: readonly ResourceLayer[];
22
+ /** Whether the routes are registered and the controller constructed. */
23
+ readonly register: boolean;
24
+ }
25
+ /** The {@link ResourcePlan} of each schematic. */
26
+ export declare function planResource(schematic: ResourceSchematic): ResourcePlan;
27
+ //# sourceMappingURL=resource.plan.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * zudojs-cli — Which files a resource-family schematic writes.
3
+ *
4
+ * `resource`, `route`, `controller`, `repository` and `dto` all write part
5
+ * of the same DTO → repository → service → controller → routes chain. Each
6
+ * writes its own layer ("primary") and any lower layer that does not exist
7
+ * yet ("required"), so whatever it writes compiles on its own:
8
+ * `generate controller users` also writes the users service, repository
9
+ * and DTO when they are missing, and never touches them when they exist.
10
+ */
11
+ /** Schematics served by the resource generator. */
12
+ export const RESOURCE_SCHEMATICS = [
13
+ "resource",
14
+ "route",
15
+ "controller",
16
+ "repository",
17
+ "dto",
18
+ ];
19
+ /** Whether `schematic` is served by the resource generator. */
20
+ export function isResourceSchematic(schematic) {
21
+ return RESOURCE_SCHEMATICS.includes(schematic);
22
+ }
23
+ /** The {@link ResourcePlan} of each schematic. */
24
+ export function planResource(schematic) {
25
+ switch (schematic) {
26
+ case "resource":
27
+ return {
28
+ primary: ["dto", "repository", "service", "controller", "routes", "test"],
29
+ required: [],
30
+ register: true,
31
+ };
32
+ case "route":
33
+ return {
34
+ primary: ["routes"],
35
+ required: ["dto", "repository", "service", "controller"],
36
+ register: true,
37
+ };
38
+ case "controller":
39
+ return { primary: ["controller"], required: ["dto", "repository", "service"], register: false };
40
+ case "repository":
41
+ return { primary: ["repository"], required: ["dto"], register: false };
42
+ case "dto":
43
+ return { primary: ["dto"], required: [], register: false };
44
+ }
45
+ }
46
+ //# sourceMappingURL=resource.plan.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * zudojs-cli — Running a resource-family schematic for `zudojs generate`.
3
+ */
4
+ import type { CLIContext } from "../../cliType/cliType.type.js";
5
+ import type { ResourceSchematic } from "./resource.plan.js";
6
+ /** Inputs of {@link runResourceSchematic}. */
7
+ export interface ResourceSchematicRun {
8
+ readonly schematic: ResourceSchematic;
9
+ readonly name: string;
10
+ readonly cwd: string;
11
+ /** `""` or `"apps/api"` (fullstack). */
12
+ readonly backendRoot: string;
13
+ readonly architecture: string | undefined;
14
+ readonly service?: string;
15
+ readonly moduleName?: string;
16
+ readonly dryRun: boolean;
17
+ readonly force: boolean;
18
+ }
19
+ /**
20
+ * Runs a resource-family schematic: writes the DTO → repository → service
21
+ * → controller → routes chain it needs and registers the routes and the
22
+ * controller between the project's markers.
23
+ */
24
+ export declare function runResourceSchematic(context: CLIContext, run: ResourceSchematicRun): Promise<void>;
25
+ //# sourceMappingURL=resource.run.d.ts.map
@@ -0,0 +1,52 @@
1
+ /**
2
+ * zudojs-cli — Running a resource-family schematic for `zudojs generate`.
3
+ */
4
+ import { describeError } from "../../commands/commandError.helper.js";
5
+ import { CLIGenerationError, CLIValidationError } from "../../errors/index.js";
6
+ import { generateResource } from "./resource.generator.js";
7
+ import { resolveResourceLayout } from "./resource.layout.js";
8
+ import { describeAddedDependencies, ensureZudojsDependencies, fileCount, } from "../../wiring/index.js";
9
+ /**
10
+ * Runs a resource-family schematic: writes the DTO → repository → service
11
+ * → controller → routes chain it needs and registers the routes and the
12
+ * controller between the project's markers.
13
+ */
14
+ export async function runResourceSchematic(context, run) {
15
+ const layout = resolveResourceLayout({
16
+ cwd: run.cwd,
17
+ architecture: run.architecture,
18
+ backendRoot: run.backendRoot,
19
+ ...(run.service !== undefined ? { service: run.service } : {}),
20
+ ...(run.moduleName !== undefined ? { module: run.moduleName } : {}),
21
+ });
22
+ let result;
23
+ try {
24
+ result = await generateResource({
25
+ name: run.name,
26
+ schematic: run.schematic,
27
+ layout,
28
+ dryRun: run.dryRun,
29
+ force: run.force,
30
+ }, run.cwd);
31
+ }
32
+ catch (error) {
33
+ if (error instanceof CLIValidationError)
34
+ throw error;
35
+ throw new CLIGenerationError(`Failed to generate ${run.schematic} "${run.name}": ${describeError(error)}`, error);
36
+ }
37
+ context.logger.info(run.dryRun
38
+ ? `Dry run: ${fileCount(result.files.length)} would be written or updated (nothing written):`
39
+ : `Generated ${fileCount(result.files.length)}:`);
40
+ for (const file of result.files) {
41
+ context.logger.info(` - ${file}`);
42
+ }
43
+ if (!run.dryRun) {
44
+ for (const line of describeAddedDependencies(ensureZudojsDependencies(run.cwd, result.files))) {
45
+ context.logger.warn(line);
46
+ }
47
+ }
48
+ if (result.manualSteps.length > 0) {
49
+ context.logger.warn(`Finish by hand:\n${result.manualSteps.map((step) => ` - ${step}`).join("\n")}`);
50
+ }
51
+ }
52
+ //# sourceMappingURL=resource.run.js.map
@@ -11,8 +11,12 @@
11
11
  * an abort was indistinguishable from a success. The exit status is now 130,
12
12
  * the conventional "terminated by SIGINT" value.
13
13
  *
14
+ * The return type strips `symbol` from the argument's type rather than
15
+ * inferring `T` from `T | symbol`: TypeScript 7 infers the clack cancel
16
+ * symbol into `T` from that pattern, which broke every call site.
17
+ *
14
18
  * @param value - The value a `@clack/prompts` call resolved with.
15
19
  * @returns The answer, once it is known not to be the cancel symbol.
16
20
  */
17
- export declare function cancelled<T>(value: T | symbol): T;
21
+ export declare function cancelled<T>(value: T): Exclude<T, symbol>;
18
22
  //# sourceMappingURL=cancel.prompt.d.ts.map
@@ -13,6 +13,10 @@ import { CLI_EXIT_CODES } from "../cliConstant/cliConstant.value.js";
13
13
  * an abort was indistinguishable from a success. The exit status is now 130,
14
14
  * the conventional "terminated by SIGINT" value.
15
15
  *
16
+ * The return type strips `symbol` from the argument's type rather than
17
+ * inferring `T` from `T | symbol`: TypeScript 7 infers the clack cancel
18
+ * symbol into `T` from that pattern, which broke every call site.
19
+ *
16
20
  * @param value - The value a `@clack/prompts` call resolved with.
17
21
  * @returns The answer, once it is known not to be the cancel symbol.
18
22
  */
@@ -0,0 +1,13 @@
1
+ /**
2
+ * zudojs-cli — Interactive Menu
3
+ *
4
+ * The numbered menu a bare `zudojs` / `zudo` opens on an interactive
5
+ * terminal: entries, the digit-or-arrow select prompt, the flow that turns
6
+ * a choice into a command line, and the terminal prompter.
7
+ */
8
+ export { MENU_CANCEL, type MenuCancel, type MenuFlowOptions, type MenuItem, type MenuItemId, type MenuOutcome, type MenuProjectScope, type MenuPrompter, } from "./menu.type.js";
9
+ export { MENU_CANCELLED, MENU_ITEMS, MENU_MESSAGE, MENU_NAME_PATTERN, } from "./menu.constant.js";
10
+ export { numberedSelect, type NumberedSelectIO } from "./menu.select.js";
11
+ export { notInProjectMessage, runMenuFlow } from "./menu.flow.js";
12
+ export { createTerminalMenuPrompter } from "./menu.adapter.js";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * zudojs-cli — Interactive Menu
3
+ *
4
+ * The numbered menu a bare `zudojs` / `zudo` opens on an interactive
5
+ * terminal: entries, the digit-or-arrow select prompt, the flow that turns
6
+ * a choice into a command line, and the terminal prompter.
7
+ */
8
+ export { MENU_CANCEL, } from "./menu.type.js";
9
+ export { MENU_CANCELLED, MENU_ITEMS, MENU_MESSAGE, MENU_NAME_PATTERN, } from "./menu.constant.js";
10
+ export { numberedSelect } from "./menu.select.js";
11
+ export { notInProjectMessage, runMenuFlow } from "./menu.flow.js";
12
+ export { createTerminalMenuPrompter } from "./menu.adapter.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * zudojs-cli — Menu Prompter
3
+ *
4
+ * The terminal implementation of `MenuPrompter`: the numbered select for
5
+ * the menu itself and `@clack/prompts` for the follow-up questions.
6
+ */
7
+ import { type NumberedSelectIO } from "./menu.select.js";
8
+ import { type MenuPrompter } from "./menu.type.js";
9
+ /**
10
+ * Creates the prompter the binary uses.
11
+ *
12
+ * @param io - Streams to read keys from and draw on; defaults to the
13
+ * process's stdin and stdout.
14
+ */
15
+ export declare function createTerminalMenuPrompter(io?: NumberedSelectIO): MenuPrompter;
16
+ //# sourceMappingURL=menu.adapter.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * zudojs-cli — Menu Prompter
3
+ *
4
+ * The terminal implementation of `MenuPrompter`: the numbered select for
5
+ * the menu itself and `@clack/prompts` for the follow-up questions.
6
+ */
7
+ import * as p from "@clack/prompts";
8
+ import { numberedSelect } from "./menu.select.js";
9
+ import { MENU_CANCEL, } from "./menu.type.js";
10
+ /**
11
+ * Maps clack's cancel symbol onto the menu's own sentinel.
12
+ *
13
+ * Both follow-up prompts answer with a string, so anything else is the
14
+ * cancel symbol; narrowing on `string` avoids TypeScript 7 inferring the
15
+ * clack symbol into a generic parameter (see `cancelled`).
16
+ */
17
+ function orCancel(value) {
18
+ return typeof value === "string" && !p.isCancel(value) ? value : MENU_CANCEL;
19
+ }
20
+ /**
21
+ * Creates the prompter the binary uses.
22
+ *
23
+ * @param io - Streams to read keys from and draw on; defaults to the
24
+ * process's stdin and stdout.
25
+ */
26
+ export function createTerminalMenuPrompter(io = { input: process.stdin, output: process.stdout }) {
27
+ return {
28
+ choose: (items) => numberedSelect(items, io),
29
+ async select(message, choices) {
30
+ const value = await p.select({
31
+ message,
32
+ options: choices.map((choice) => ({
33
+ value: choice.value,
34
+ label: choice.label,
35
+ ...(choice.hint ? { hint: choice.hint } : {}),
36
+ })),
37
+ });
38
+ return orCancel(value);
39
+ },
40
+ async text(message, placeholder, validate) {
41
+ const value = await p.text({
42
+ message,
43
+ placeholder,
44
+ validate: (input) => validate(input ?? ""),
45
+ });
46
+ return orCancel(value);
47
+ },
48
+ warn(message) {
49
+ p.log.warn(message);
50
+ },
51
+ cancel(message) {
52
+ p.cancel(message);
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=menu.adapter.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * zudojs-cli — Menu Entries
3
+ *
4
+ * The numbered entries of the interactive menu, in display order.
5
+ */
6
+ import type { MenuItem } from "./menu.type.js";
7
+ /** Menu entries; `key` is the digit that selects each one. */
8
+ export declare const MENU_ITEMS: readonly MenuItem[];
9
+ /** The question the menu asks. */
10
+ export declare const MENU_MESSAGE = "What would you like to do?";
11
+ /** Printed when the menu is left with Esc or Ctrl+C. */
12
+ export declare const MENU_CANCELLED = "Operation cancelled.";
13
+ /**
14
+ * Names accepted by the "Generate code" follow-up.
15
+ *
16
+ * The same shape the project-name prompt allows: a leading "-" would be
17
+ * parsed as an option once the name is handed to `generate`.
18
+ */
19
+ export declare const MENU_NAME_PATTERN: RegExp;
20
+ //# sourceMappingURL=menu.constant.d.ts.map