create-win-project 1.3.0 → 1.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.
Files changed (232) hide show
  1. package/README.md +19 -28
  2. package/{scripts/compatibility-matrix.mjs → checks/check-compatibility.js} +24 -6
  3. package/{scripts/verify-generated.mjs → checks/check-generated-project.js} +2 -2
  4. package/{scripts/validate-content.mjs → checks/check-library.js} +5 -5
  5. package/checks/classify-changes.js +39 -0
  6. package/docs/ARCHITECTURE.md +83 -40
  7. package/docs/CONTENT_MODEL.md +3 -3
  8. package/docs/CONTRIBUTING.md +154 -0
  9. package/docs/DEPENDENCY_MAINTENANCE.md +1 -1
  10. package/index.js +2 -429
  11. package/{playbooks → library}/INDEX.md +4 -4
  12. package/{playbooks/capabilities/ci/github-actions.manifest.json → library/development-tools/ci/definition.json} +1 -0
  13. package/{playbooks/devops/makefile.manifest.json → library/development-tools/devops/makefile/definition.json} +1 -0
  14. package/{playbooks/devops/pr-template.manifest.json → library/development-tools/devops/pr-template/definition.json} +1 -0
  15. package/{playbooks/platform/mobile.manifest.json → library/platforms/mobile/definition.json} +1 -0
  16. package/{playbooks/platform/web.manifest.json → library/platforms/web/definition.json} +1 -0
  17. package/{compatibility/profiles.json → library/tested-versions.json} +4 -0
  18. package/{playbooks/universal/accessibility.manifest.json → library/universal/accessibility/definition.json} +1 -1
  19. package/{playbooks/universal/coding-rules.manifest.json → library/universal/coding-rules/definition.json} +1 -0
  20. package/{playbooks/universal/error-handling.manifest.json → library/universal/error-handling/definition.json} +1 -0
  21. package/{playbooks/universal/git-conventions.manifest.json → library/universal/git-conventions/definition.json} +1 -0
  22. package/{playbooks/universal/observability.manifest.json → library/universal/observability/definition.json} +1 -0
  23. package/{playbooks/universal/security.manifest.json → library/universal/security/definition.json} +1 -0
  24. package/{playbooks/universal/typescript.manifest.json → library/universal/typescript/definition.json} +1 -0
  25. package/package.json +12 -11
  26. package/public/logo.svg +19 -0
  27. package/src/cli/arguments.js +52 -0
  28. package/src/cli/display.js +220 -0
  29. package/src/cli/main.js +185 -0
  30. package/src/cli/navigation.js +156 -0
  31. package/src/cli/questions.js +187 -0
  32. package/src/cli/system-check.js +138 -0
  33. package/src/engine/create-project.js +10 -0
  34. package/src/engine/install-dependencies.js +13 -0
  35. package/{lib/catalog.js → src/engine/load-library.js} +5 -6
  36. package/{lib/playbooks.js → src/engine/project-guidance.js} +25 -3
  37. package/{lib/template.js → src/engine/render-templates.js} +8 -0
  38. package/src/engine/write-files.js +34 -0
  39. package/{lib/stacks/registry.js → src/stacks/available-stacks.js} +23 -1
  40. package/src/stacks/backends/README.md +5 -0
  41. package/{lib/stacks → src/stacks/backends}/laravel/architecture.js +1 -0
  42. package/{lib/stacks → src/stacks/backends}/laravel/auth/oidc.js +1 -0
  43. package/{lib/stacks → src/stacks/backends}/laravel/auth/public.js +1 -0
  44. package/{lib/stacks → src/stacks/backends}/laravel/auth/sanctum.js +1 -0
  45. package/{lib/stacks → src/stacks/backends}/laravel/auth/session.js +1 -0
  46. package/src/stacks/backends/laravel/ci.js +3 -0
  47. package/{lib/stacks → src/stacks/backends}/laravel/composer.js +1 -1
  48. package/{lib/stacks/laravel/generate.js → src/stacks/backends/laravel/create-files.js} +1 -0
  49. package/src/stacks/backends/laravel/docker.js +14 -0
  50. package/src/stacks/backends/laravel/environment.js +5 -0
  51. package/{lib/stacks → src/stacks/backends}/laravel/index.js +11 -3
  52. package/{lib/stacks → src/stacks/backends}/laravel/ui/blade.js +1 -0
  53. package/{lib/stacks → src/stacks/backends}/laravel/ui/index.js +1 -0
  54. package/{lib/stacks → src/stacks/backends}/laravel/ui/inertia-react.js +4 -2
  55. package/{lib/stacks → src/stacks/backends}/laravel/ui/livewire.js +1 -0
  56. package/{lib/stacks → src/stacks/backends}/laravel/ui/shared.js +1 -0
  57. package/src/stacks/backends/none/index.js +23 -0
  58. package/src/stacks/backends/postgres/ci.js +3 -0
  59. package/src/stacks/backends/postgres/create-files.js +21 -0
  60. package/src/stacks/backends/postgres/docker.js +3 -0
  61. package/src/stacks/backends/postgres/environment.js +3 -0
  62. package/src/stacks/backends/postgres/index.js +30 -0
  63. package/src/stacks/backends/springboot/ci.js +3 -0
  64. package/src/stacks/backends/springboot/create-files.js +91 -0
  65. package/src/stacks/backends/springboot/docker.js +3 -0
  66. package/src/stacks/backends/springboot/environment.js +3 -0
  67. package/src/stacks/backends/springboot/index.js +31 -0
  68. package/src/stacks/backends/supabase/ci.js +3 -0
  69. package/src/stacks/backends/supabase/create-files.js +52 -0
  70. package/src/stacks/backends/supabase/docker.js +3 -0
  71. package/src/stacks/backends/supabase/environment.js +3 -0
  72. package/src/stacks/backends/supabase/index.js +31 -0
  73. package/src/stacks/backends/supabase/native.js +14 -0
  74. package/src/stacks/compose-files.js +241 -0
  75. package/{lib/generator.js → src/stacks/create-project.js} +78 -50
  76. package/src/stacks/frontends/README.md +4 -0
  77. package/src/stacks/frontends/nextjs/ci.js +3 -0
  78. package/src/stacks/frontends/nextjs/create-files.js +46 -0
  79. package/src/stacks/frontends/nextjs/dependencies.js +5 -0
  80. package/src/stacks/frontends/nextjs/docker.js +3 -0
  81. package/src/stacks/frontends/nextjs/environment.js +7 -0
  82. package/src/stacks/frontends/nextjs/index.js +35 -0
  83. package/src/stacks/frontends/react-native/ci.js +3 -0
  84. package/src/stacks/frontends/react-native/create-files.js +25 -0
  85. package/src/stacks/frontends/react-native/dependencies.js +5 -0
  86. package/src/stacks/frontends/react-native/docker.js +5 -0
  87. package/src/stacks/frontends/react-native/environment.js +7 -0
  88. package/src/stacks/frontends/react-native/index.js +30 -0
  89. package/src/stacks/frontends/react-vite/ci.js +3 -0
  90. package/src/stacks/frontends/react-vite/create-files.js +27 -0
  91. package/src/stacks/frontends/react-vite/dependencies.js +5 -0
  92. package/src/stacks/frontends/react-vite/docker.js +3 -0
  93. package/src/stacks/frontends/react-vite/environment.js +8 -0
  94. package/src/stacks/frontends/react-vite/index.js +32 -0
  95. package/{lib → src}/stacks/shared/contributions.js +1 -1
  96. package/src/stacks/shared/environment.js +30 -0
  97. package/src/stacks/shared/javascript-package.js +111 -0
  98. package/src/stacks/shared/testing-files.js +16 -0
  99. package/templates/docker/compose/postgres.yml +1 -1
  100. package/templates/docker/compose/springboot.yml +3 -3
  101. package/templates/docker/compose/supabase.yml +1 -1
  102. package/templates/docker/compose-prod/springboot.yml +1 -1
  103. package/lib/banner.js +0 -45
  104. package/lib/constants.js +0 -3
  105. package/lib/doctor.js +0 -45
  106. package/lib/interview.js +0 -77
  107. package/lib/laravel-scaffold.js +0 -3
  108. package/lib/scaffold.js +0 -467
  109. package/lib/stacks/index.js +0 -8
  110. /package/{playbooks/capabilities → library/development-tools}/ci/github-actions.md +0 -0
  111. /package/{playbooks/devops → library/development-tools/devops/makefile}/makefile.md +0 -0
  112. /package/{playbooks/devops → library/development-tools/devops/pr-template}/pr-template.md +0 -0
  113. /package/{playbooks/capabilities/docker/docker.manifest.json → library/development-tools/docker/definition.json} +0 -0
  114. /package/{playbooks/capabilities → library/development-tools}/docker/overview.md +0 -0
  115. /package/{playbooks/capabilities → library/features}/auth/oidc-resource-server.md +0 -0
  116. /package/{playbooks/capabilities → library/features}/auth/spring-session.md +0 -0
  117. /package/{playbooks/capabilities/flyway/flyway.manifest.json → library/features/flyway/definition.json} +0 -0
  118. /package/{playbooks/capabilities → library/features}/flyway/environments.md +0 -0
  119. /package/{playbooks/capabilities → library/features}/flyway/migrations.md +0 -0
  120. /package/{playbooks/capabilities → library/features}/flyway/testing.md +0 -0
  121. /package/{playbooks/capabilities → library/features}/laravel/database.md +0 -0
  122. /package/{playbooks/capabilities → library/features}/laravel/migrations.md +0 -0
  123. /package/{playbooks/capabilities → library/features}/laravel/observability.md +0 -0
  124. /package/{playbooks/capabilities → library/features}/laravel/oidc-resource-server.md +0 -0
  125. /package/{playbooks/capabilities → library/features}/laravel/queues.md +0 -0
  126. /package/{playbooks/capabilities → library/features}/laravel/sanctum-spa.md +0 -0
  127. /package/{playbooks/capabilities → library/features}/laravel/scheduler.md +0 -0
  128. /package/{playbooks/capabilities → library/features}/laravel/session-auth.md +0 -0
  129. /package/{playbooks/capabilities → library/features}/laravel/storage-uploads.md +0 -0
  130. /package/{playbooks/capabilities → library/features}/postgresql/architecture.md +0 -0
  131. /package/{playbooks/capabilities/postgresql/postgresql.manifest.json → library/features/postgresql/definition.json} +0 -0
  132. /package/{playbooks/capabilities → library/features}/postgresql/migrations.md +0 -0
  133. /package/{playbooks/capabilities → library/features}/postgresql/schema-design.md +0 -0
  134. /package/{playbooks/capabilities → library/features}/postgresql/security.md +0 -0
  135. /package/{playbooks/capabilities → library/features}/postgresql/testing.md +0 -0
  136. /package/{playbooks/capabilities → library/features}/prisma/architecture.md +0 -0
  137. /package/{playbooks/capabilities/prisma/prisma.manifest.json → library/features/prisma/definition.json} +0 -0
  138. /package/{playbooks/capabilities → library/features}/prisma/migrations.md +0 -0
  139. /package/{playbooks/capabilities → library/features}/prisma/runtime.md +0 -0
  140. /package/{playbooks/capabilities → library/features}/prisma/schema.md +0 -0
  141. /package/{playbooks/capabilities → library/features}/prisma/testing.md +0 -0
  142. /package/{playbooks/capabilities → library/features}/supabase/architecture.md +0 -0
  143. /package/{playbooks/capabilities → library/features}/supabase/authentication.md +0 -0
  144. /package/{playbooks/capabilities/supabase/supabase.manifest.json → library/features/supabase/definition.json} +0 -0
  145. /package/{playbooks/capabilities → library/features}/supabase/expo.md +0 -0
  146. /package/{playbooks/capabilities → library/features}/supabase/migrations.md +0 -0
  147. /package/{playbooks/capabilities → library/features}/supabase/nextjs.md +0 -0
  148. /package/{playbooks/capabilities → library/features}/supabase/rls.md +0 -0
  149. /package/{playbooks/capabilities → library/features}/supabase/testing.md +0 -0
  150. /package/{playbooks/capabilities → library/features}/supabase/vite.md +0 -0
  151. /package/{playbooks → library/optional-features}/concerns/axios.md +0 -0
  152. /package/{playbooks → library/optional-features}/concerns/next-safe-action.md +0 -0
  153. /package/{playbooks → library/optional-features}/concerns/next-themes.md +0 -0
  154. /package/{playbooks → library/optional-features}/concerns/nuqs.md +0 -0
  155. /package/{playbooks → library/optional-features}/concerns/t3-env.md +0 -0
  156. /package/{playbooks → library/optional-features}/concerns/tanstack-query.md +0 -0
  157. /package/{playbooks → library/optional-features}/concerns/zod.md +0 -0
  158. /package/{playbooks → library/optional-features}/concerns/zustand.md +0 -0
  159. /package/{playbooks/styling → library/optional-features/styling/css-modules}/css-modules-extensions.md +0 -0
  160. /package/{playbooks/styling/css-modules.manifest.json → library/optional-features/styling/css-modules/definition.json} +0 -0
  161. /package/{playbooks/styling/native-styles.manifest.json → library/optional-features/styling/native-styles/definition.json} +0 -0
  162. /package/{playbooks/styling → library/optional-features/styling/native-styles}/native-styles.md +0 -0
  163. /package/{playbooks/styling/tailwind.manifest.json → library/optional-features/styling/tailwind/definition.json} +0 -0
  164. /package/{playbooks/styling → library/optional-features/styling/tailwind}/tailwind-extensions.md +0 -0
  165. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/architecture.md +0 -0
  166. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/runtime.md +0 -0
  167. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/security.md +0 -0
  168. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/structure.md +0 -0
  169. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/testing.md +0 -0
  170. /package/{playbooks/stack/laravel-ui.manifest.json → library/platforms/laravel-ui/definition.json} +0 -0
  171. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/architecture.md +0 -0
  172. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/runtime.md +0 -0
  173. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/security.md +0 -0
  174. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/structure.md +0 -0
  175. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/testing.md +0 -0
  176. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/architecture.md +0 -0
  177. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/runtime.md +0 -0
  178. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/security.md +0 -0
  179. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/structure.md +0 -0
  180. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/testing.md +0 -0
  181. /package/{playbooks/platform → library/platforms/mobile}/mobile.md +0 -0
  182. /package/{playbooks/platform → library/platforms/web}/web.md +0 -0
  183. /package/{playbooks/stack → library/stacks}/expo/architecture.md +0 -0
  184. /package/{playbooks/stack/react-native.manifest.json → library/stacks/expo/definition.json} +0 -0
  185. /package/{playbooks/stack → library/stacks}/expo/runtime.md +0 -0
  186. /package/{playbooks/stack → library/stacks}/expo/security.md +0 -0
  187. /package/{playbooks/stack → library/stacks}/expo/structure.md +0 -0
  188. /package/{playbooks/stack → library/stacks}/expo/testing.md +0 -0
  189. /package/{playbooks/stack → library/stacks}/laravel/architecture.md +0 -0
  190. /package/{playbooks/stack/laravel.manifest.json → library/stacks/laravel/definition.json} +0 -0
  191. /package/{playbooks/stack → library/stacks}/laravel/runtime.md +0 -0
  192. /package/{playbooks/stack → library/stacks}/laravel/security.md +0 -0
  193. /package/{playbooks/stack → library/stacks}/laravel/structure.md +0 -0
  194. /package/{playbooks/stack → library/stacks}/laravel/testing.md +0 -0
  195. /package/{playbooks/stack → library/stacks}/nextjs/architecture.md +0 -0
  196. /package/{playbooks/stack/nextjs.manifest.json → library/stacks/nextjs/definition.json} +0 -0
  197. /package/{playbooks/stack → library/stacks}/nextjs/runtime.md +0 -0
  198. /package/{playbooks/stack → library/stacks}/nextjs/security.md +0 -0
  199. /package/{playbooks/stack → library/stacks}/nextjs/structure.md +0 -0
  200. /package/{playbooks/stack → library/stacks}/nextjs/testing.md +0 -0
  201. /package/{playbooks/stack/no-frontend.manifest.json → library/stacks/no-frontend/definition.json} +0 -0
  202. /package/{playbooks/stack/none.manifest.json → library/stacks/none/definition.json} +0 -0
  203. /package/{playbooks/stack → library/stacks}/react-vite/architecture.md +0 -0
  204. /package/{playbooks/stack/react-vite.manifest.json → library/stacks/react-vite/definition.json} +0 -0
  205. /package/{playbooks/stack → library/stacks}/react-vite/runtime.md +0 -0
  206. /package/{playbooks/stack → library/stacks}/react-vite/security.md +0 -0
  207. /package/{playbooks/stack → library/stacks}/react-vite/structure.md +0 -0
  208. /package/{playbooks/stack → library/stacks}/react-vite/testing.md +0 -0
  209. /package/{playbooks/stack → library/stacks}/springboot/architecture.md +0 -0
  210. /package/{playbooks/stack/springboot.manifest.json → library/stacks/springboot/definition.json} +0 -0
  211. /package/{playbooks/stack → library/stacks}/springboot/runtime.md +0 -0
  212. /package/{playbooks/stack → library/stacks}/springboot/security.md +0 -0
  213. /package/{playbooks/stack → library/stacks}/springboot/structure.md +0 -0
  214. /package/{playbooks/stack → library/stacks}/springboot/testing.md +0 -0
  215. /package/{playbooks/universal → library/universal/accessibility}/accessibility.md +0 -0
  216. /package/{playbooks/universal → library/universal/coding-rules}/coding-rules.md +0 -0
  217. /package/{playbooks/universal → library/universal/error-handling}/error-handling.md +0 -0
  218. /package/{playbooks/universal → library/universal/git-conventions}/git-conventions.md +0 -0
  219. /package/{playbooks/universal → library/universal/observability}/observability.md +0 -0
  220. /package/{playbooks/universal → library/universal/security}/security.md +0 -0
  221. /package/{playbooks/universal → library/universal/typescript}/typescript.md +0 -0
  222. /package/{lib/files.js → src/engine/project-files.js} +0 -0
  223. /package/{lib → src/engine}/project-location.js +0 -0
  224. /package/{lib/application-shapes.js → src/engine/project-shapes.js} +0 -0
  225. /package/{lib/compatibility.js → src/engine/tested-versions.js} +0 -0
  226. /package/{lib → src}/stacks/context.js +0 -0
  227. /package/{lib/stacks/contract.js → src/stacks/rules.js} +0 -0
  228. /package/{ci → templates/ci}/expo.yml +0 -0
  229. /package/{ci → templates/ci}/laravel.yml +0 -0
  230. /package/{ci → templates/ci}/nextjs.yml +0 -0
  231. /package/{ci → templates/ci}/springboot.yml +0 -0
  232. /package/{ci → templates/ci}/vite.yml +0 -0
@@ -2,6 +2,7 @@
2
2
  "id": "web-platform",
3
3
  "kind": "platform",
4
4
  "label": "Web Platform",
5
+ "file": "platform/web.md",
5
6
  "appliesTo": { "platform": ["web"] },
6
7
  "deps": [],
7
8
  "devDeps": [],
@@ -9,6 +9,8 @@
9
9
  "springModulith": "2.0.8",
10
10
  "runtimes": {
11
11
  "node": "24.20.0",
12
+ "nodeMinimum": "22.14.0",
13
+ "npmMinimum": "11.19.0",
12
14
  "java": "21",
13
15
  "maven": "3.9.16",
14
16
  "postgres": "16",
@@ -125,6 +127,8 @@
125
127
  "springModulith": "2.0.8",
126
128
  "runtimes": {
127
129
  "node": "24.20.0",
130
+ "nodeMinimum": "22.14.0",
131
+ "npmMinimum": "11.19.0",
128
132
  "java": "21",
129
133
  "maven": "3.9.16",
130
134
  "postgres": "16",
@@ -2,10 +2,10 @@
2
2
  "id": "accessibility-baseline",
3
3
  "kind": "universal",
4
4
  "label": "Accessibility Baseline",
5
+ "file": "universal/accessibility.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
8
9
  { "id": "accessibility", "required": true, "sections": ["Semantic Structure", "Keyboard and Focus", "Forms and Authentication", "Accessibility Testing"] }
9
10
  ]
10
11
  }
11
-
@@ -2,6 +2,7 @@
2
2
  "id": "coding-rules",
3
3
  "kind": "universal",
4
4
  "label": "Coding Rules",
5
+ "file": "universal/coding-rules.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "devDeps": ["prettier"],
@@ -2,6 +2,7 @@
2
2
  "id": "error-handling",
3
3
  "kind": "universal",
4
4
  "label": "Error Handling",
5
+ "file": "universal/error-handling.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
@@ -2,6 +2,7 @@
2
2
  "id": "git-conventions",
3
3
  "kind": "universal",
4
4
  "label": "Git Conventions",
5
+ "file": "universal/git-conventions.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
@@ -2,6 +2,7 @@
2
2
  "id": "observability-baseline",
3
3
  "kind": "universal",
4
4
  "label": "Observability Baseline",
5
+ "file": "universal/observability.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
@@ -2,6 +2,7 @@
2
2
  "id": "security-baseline",
3
3
  "kind": "universal",
4
4
  "label": "Security Baseline",
5
+ "file": "universal/security.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
@@ -2,6 +2,7 @@
2
2
  "id": "typescript",
3
3
  "kind": "universal",
4
4
  "label": "TypeScript",
5
+ "file": "universal/typescript.md",
5
6
  "required": true,
6
7
  "folders": [],
7
8
  "concerns": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-win-project",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Project scaffolding CLI by Win",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -13,15 +13,15 @@
13
13
  },
14
14
  "files": [
15
15
  "index.js",
16
- "lib",
17
- "compatibility",
18
- "playbooks",
19
- "ci",
16
+ "src",
17
+ "library",
20
18
  "templates",
21
- "scripts",
19
+ "checks",
20
+ "public/logo.svg",
22
21
  "docs/ARCHITECTURE.md",
23
22
  "docs/CONTENT_MODEL.md",
24
23
  "docs/DEPENDENCY_MAINTENANCE.md",
24
+ "docs/CONTRIBUTING.md",
25
25
  "README.md",
26
26
  "LICENSE"
27
27
  ],
@@ -29,13 +29,14 @@
29
29
  "start": "node index.js",
30
30
  "doctor": "node index.js doctor",
31
31
  "test": "vitest run",
32
- "validate:content": "node scripts/validate-content.mjs",
33
- "matrix:smoke": "node scripts/compatibility-matrix.mjs --scope=smoke",
34
- "matrix:full": "node scripts/compatibility-matrix.mjs --scope=full",
35
- "verify:generated": "node scripts/verify-generated.mjs"
32
+ "validate:content": "node checks/check-library.js",
33
+ "matrix:smoke": "node checks/check-compatibility.js --scope=smoke",
34
+ "matrix:full": "node checks/check-compatibility.js --scope=full",
35
+ "verify:generated": "node checks/check-generated-project.js"
36
36
  },
37
37
  "engines": {
38
- "node": ">=24"
38
+ "node": ">=22.14",
39
+ "npm": ">=11.19"
39
40
  },
40
41
  "dependencies": {
41
42
  "chalk": "5.6.2",
@@ -0,0 +1,19 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-label="W1N Project logo">
2
+ <title>W1N Project logo</title>
3
+ <style>
4
+ .bracket { fill: #ffffff; }
5
+ .accent { fill: #bdfa02; }
6
+ </style>
7
+ <circle cx="128" cy="128" r="128" fill="#0a0a0a" />
8
+ <g class="brackets">
9
+ <path class="bracket" d="M 90.00 183.50 L 85.50 180.00 L 81.50 162.00 L 84.50 155.00 L 128.00 93.50 L 152.00 93.50 L 153.50 96.00 L 149.50 102.00 L 90.00 183.50 Z" />
10
+ <path class="bracket" d="M 59.00 162.50 L 46.00 162.50 L 12.50 130.00 L 12.50 126.00 L 46.00 93.50 L 59.00 93.50 L 60.50 102.00 L 34.50 128.00 L 60.50 154.00 L 59.00 162.50 Z" />
11
+ <path class="bracket" d="M 209.00 162.50 L 196.00 162.50 L 194.50 154.00 L 220.50 127.00 L 194.50 102.00 L 196.00 93.50 L 211.00 94.50 L 242.50 126.00 L 241.50 131.00 L 209.00 162.50 Z" />
12
+ <path class="bracket" d="M 82.00 151.50 L 79.50 151.00 L 78.50 147.00 L 64.50 79.00 L 64.50 74.00 L 67.00 71.50 L 86.00 71.50 L 89.50 75.00 L 100.50 124.00 L 82.00 151.50 Z" />
13
+ <path class="bracket" d="M 121.00 183.50 L 114.00 183.50 L 111.50 181.00 L 108.50 163.00 L 124.50 141.00 L 127.00 140.50 L 132.50 162.00 L 121.00 183.50 Z" />
14
+ </g>
15
+ <g class="accents">
16
+ <path class="accent" d="M 149.00 183.50 L 128.00 183.50 L 126.50 180.00 L 165.50 91.00 L 150.50 89.00 L 171.00 71.50 L 195.00 71.50 L 195.50 78.00 L 149.00 183.50 Z" />
17
+ <path class="accent" d="M 225.00 183.50 L 173.00 183.50 L 171.50 174.00 L 225.00 172.50 L 225.00 183.50 Z" />
18
+ </g>
19
+ </svg>
@@ -0,0 +1,52 @@
1
+ import { APPLICATION_SHAPES } from '../engine/project-shapes.js'
2
+ import { laravelUis } from '../stacks/backends/laravel/ui/index.js'
3
+
4
+ function option(args, name) {
5
+ return args.find((argument) => argument.startsWith(`--${name}=`))?.split('=')[1]
6
+ }
7
+
8
+ export function parseArguments(args) {
9
+ const profile = option(args, 'profile')
10
+ const shape = option(args, 'shape')
11
+ const frontendValue = option(args, 'frontend')
12
+ const frontendAliases = { vite: 'react', expo: 'react-native', none: 'no-frontend' }
13
+ const frontend = frontendAliases[frontendValue] || frontendValue
14
+ const backend = option(args, 'backend')
15
+ const architecture = option(args, 'architecture')
16
+ const authentication = option(args, 'authentication')
17
+ const authAudience = option(args, 'auth-audience')
18
+ const laravelUi = option(args, 'laravel-ui')
19
+ const install = args.includes('--install')
20
+ const noInstall = args.includes('--no-install')
21
+
22
+ if (shape && !APPLICATION_SHAPES[shape]) {
23
+ throw new Error('--shape must be fullstack, separate, api, mobile, or frontend')
24
+ }
25
+ if (architecture && !['small', 'medium', 'large'].includes(architecture)) {
26
+ throw new Error('--architecture must be small, medium, or large')
27
+ }
28
+ if (authentication && !['yes', 'not-yet', 'none'].includes(authentication)) {
29
+ throw new Error('--authentication must be yes, not-yet, or none')
30
+ }
31
+ if (authAudience && !['website', 'multi-client'].includes(authAudience)) {
32
+ throw new Error('--auth-audience must be website or multi-client')
33
+ }
34
+ if (laravelUi && !laravelUis.some((ui) => ui.id === laravelUi)) {
35
+ throw new Error('--laravel-ui must be blade, livewire, or inertia-react')
36
+ }
37
+ if (install && noInstall) throw new Error('Use either --install or --no-install, not both')
38
+
39
+ return Object.freeze({
40
+ profile,
41
+ shape,
42
+ frontend,
43
+ backend,
44
+ architecture,
45
+ authentication,
46
+ authAudience,
47
+ laravelUi,
48
+ install,
49
+ noInstall,
50
+ doctor: args[0] === 'doctor' || args.includes('--doctor'),
51
+ })
52
+ }
@@ -0,0 +1,220 @@
1
+ import chalk from 'chalk'
2
+ import ora from 'ora'
3
+ import { APPLICATION_SHAPES } from '../engine/project-shapes.js'
4
+ import path from 'node:path'
5
+
6
+ // ─── Banner (from lib/banner.js) ────────────────────────────────────────────
7
+ const W1N = [
8
+ ' ██╗ ██╗ ██╗ ███╗ ██╗',
9
+ ' ██║ ██║ ███║ ████╗ ██║',
10
+ ' ██║ █╗ ██║ ╚██║ ██╔██╗██║',
11
+ ' ██║███╗██║ ██║ ██║╚████║',
12
+ ' ╚███╔███╔╝ ██║ ██║ ╚███║',
13
+ ' ╚══╝╚══╝ ╚═╝ ╚═╝ ╚══╝',
14
+ ]
15
+ const PROJECT = [
16
+ ' █▀█ █▀█ █▀█ █ █▀▀ █▀▀ ▀█▀',
17
+ ' █▀▀ █▀█ █ █ █ █▀ █ █ ',
18
+ ' █ █ █ █▄█ █▄█ █▄▄ █▄▄ █ ',
19
+ ]
20
+ function plain() {
21
+ return [...W1N, '', ...PROJECT].join('\n')
22
+ }
23
+ function colored() {
24
+ const w1n = W1N.map((l) => chalk.bold.cyan(l)).join('\n')
25
+ const proj = PROJECT.map((l) => chalk.bold.white(l)).join('\n')
26
+ return `${w1n}\n${proj}`
27
+ }
28
+ export function w1nBanner() {
29
+ const noColor = process.env.NO_COLOR !== undefined || process.env.FORCE_COLOR === '0'
30
+ const isTTY = process.stdout.isTTY
31
+ const width = process.stdout.columns || 80
32
+ if (!isTTY || width < 80 || noColor) {
33
+ if (width < 60) return 'W1N PROJECT'
34
+ return plain()
35
+ }
36
+ return colored()
37
+ }
38
+ export const bannerRaw = plain()
39
+ export const W1N_ART = W1N.join('\n')
40
+ export const PROJECT_ART = PROJECT.join('\n')
41
+
42
+ // ─── Project location (from src/engine/project-location.js) ─────────────────
43
+ export function isInsideDirectory(candidate, parent) {
44
+ const relative = path.relative(path.resolve(parent), path.resolve(candidate))
45
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
46
+ }
47
+ export function projectLocationNotice({ cwd, cliRoot, projectName }) {
48
+ if (!isInsideDirectory(cwd, cliRoot)) return null
49
+ const generatedPath = path.join(path.resolve(cwd), projectName)
50
+ const suggestedPath = path.join(path.dirname(path.resolve(cliRoot)), projectName)
51
+ return {
52
+ generatedPath,
53
+ suggestedPath,
54
+ message: `This project was created inside the create-win-project repository. Move or cut the generated folder to your normal projects folder outside this repository before you start committing application work.`,
55
+ }
56
+ }
57
+
58
+ // ─── Display helpers ─────────────────────────────────────────────────────────
59
+ export function printBanner(profile, output = console.log) {
60
+ output('')
61
+ output(w1nBanner())
62
+ output('')
63
+ output(chalk.gray(' Production-ready project scaffolding'))
64
+ output(chalk.gray(` Compatibility profile: ${profile.id} (${profile.status})`))
65
+ output('')
66
+ }
67
+
68
+ export function printCancelled({ explainNoFiles = false } = {}, output = console.log) {
69
+ output(chalk.yellow(explainNoFiles ? '\n Cancelled. No files were created.\n' : '\n Cancelled.\n'))
70
+ }
71
+
72
+ export function startProjectSpinner() {
73
+ return ora('Scaffolding project...').start()
74
+ }
75
+
76
+ export function projectSucceeded(spinner) {
77
+ spinner.succeed(chalk.green('Project created!'))
78
+ }
79
+
80
+ export function startInstallSpinner() {
81
+ return ora('Installing exact dependencies and creating lockfiles...').start()
82
+ }
83
+
84
+ export function installSucceeded(spinner) {
85
+ spinner.succeed(chalk.green('Dependencies installed and lockfiles created.'))
86
+ }
87
+
88
+ export function installFailed(spinner) {
89
+ spinner.warn(chalk.yellow('Project created, but dependency installation did not finish.'))
90
+ }
91
+
92
+ export function generationFailed(spinner, error, report = console.error) {
93
+ spinner.fail(chalk.red('Failed to generate project'))
94
+ report(error)
95
+ report(chalk.yellow('Review the error above, correct the input or environment, then run create-win-project again.'))
96
+ }
97
+
98
+ export function printSummary({ answers, stack, catalog }, output = console.log) {
99
+ output('')
100
+ output(chalk.cyan.bold(' Review before creating files'))
101
+ output(chalk.gray(' ───────────────────────────'))
102
+ output(` ${chalk.cyan('Name:')} ${answers.projectName}`)
103
+ output(` ${chalk.cyan('Stack:')} ${stack.label}`)
104
+ output(` ${chalk.cyan('Shape:')} ${APPLICATION_SHAPES[stack.applicationShape].label}`)
105
+ output(` ${chalk.cyan('Platform:')} ${stack.platform}`)
106
+ if (stack.styleId) output(` ${chalk.cyan('Styling:')} ${catalog.byId[stack.styleId]?.label || stack.styleId}`)
107
+ if (answers.laravelUi) output(` ${chalk.cyan('Laravel UI:')} ${answers.laravelUi}`)
108
+ output(` ${chalk.cyan('Architecture:')} ${stack.architecture[0].toUpperCase()}${stack.architecture.slice(1)}`)
109
+ output(` ${chalk.cyan('Authentication:')} ${stack.authentication}`)
110
+ output(` ${chalk.cyan('Testing:')} ${answers.testing}`)
111
+ if (stack.platform !== 'mobile') {
112
+ output(` ${chalk.cyan('Docker:')} ${answers.docker ? 'yes' : 'no'}`)
113
+ output(` ${chalk.cyan('Makefile:')} ${answers.makefile ? 'yes' : 'no'}`)
114
+ }
115
+ output(` ${chalk.cyan('CI/CD:')} ${answers.githubActions ? 'yes' : 'no'}`)
116
+ output(` ${chalk.cyan('Install deps:')} ${answers.installDependencies ? 'yes' : 'no'}`)
117
+ if (answers.packageName) output(` ${chalk.cyan('Package:')} ${answers.packageName}`)
118
+ if (stack.constraints.length) {
119
+ output('')
120
+ output(chalk.gray(' Key constraints:'))
121
+ for (const rule of stack.constraints.slice(0, 3)) output(chalk.gray(` • ${rule}`))
122
+ }
123
+ output('')
124
+ }
125
+
126
+ export function printRuntimeMismatch(issues, output = console.log) {
127
+ output(chalk.yellow.bold('\n Runtime mismatch\n'))
128
+ for (const issue of issues) {
129
+ output(chalk.yellow(` ${issue.tool} ${issue.required} or newer is required.`))
130
+ output(chalk.gray(` Current terminal: ${issue.tool} ${issue.found || 'not found'}.`))
131
+ }
132
+ output(chalk.gray('\n Your global tools will not be changed.\n'))
133
+ }
134
+
135
+ export function printRuntimeInstructions(lines, output = console.log) {
136
+ output(chalk.cyan.bold('\n Runtime setup\n'))
137
+ for (const line of lines) output(chalk.gray(` ${line}`))
138
+ output(chalk.gray('\n Run create-win-project again after switching runtimes.\n'))
139
+ }
140
+
141
+ export function printRuntimeSkip(issues, output = console.log) {
142
+ output(chalk.yellow('\n Dependency installation will be skipped because this terminal does not meet the selected project requirements.'))
143
+ for (const issue of issues) output(chalk.gray(` ${issue.tool}: ${issue.found || 'not found'} (requires ${issue.required} or newer)`))
144
+ output(chalk.gray(' Project files will still be created. Use Mise/NVM, then run the retry command shown below.\n'))
145
+ }
146
+
147
+ export function printLocationNotice(notice, output = console.log) {
148
+ if (!notice) return
149
+ output('')
150
+ output(chalk.yellow.bold(' Project location note'))
151
+ output(chalk.yellow(` ${notice.message}`))
152
+ output(chalk.gray(` Created at: ${notice.generatedPath}`))
153
+ output(chalk.gray(` Suggested destination: ${notice.suggestedPath}`))
154
+ output(chalk.gray(` Linux/macOS: mv "${notice.generatedPath}" "${notice.suggestedPath}"`))
155
+ output(chalk.gray(' Windows: cut the generated folder in File Explorer and paste it into your projects folder.'))
156
+ }
157
+
158
+ function detectedLine(versions) {
159
+ return ` Detected Node.js ${versions.node || 'not found'}, npm ${versions.npm || 'not found'}, PHP ${versions.php || 'not found'}, and Composer ${versions.composer || 'not found'}.`
160
+ }
161
+
162
+ export function printInstallFailure(versions, retry, output = console.log) {
163
+ output(chalk.yellow(detectedLine(versions)))
164
+ output(chalk.yellow(` Retry with: ${retry}`))
165
+ }
166
+
167
+ export function printInstallSkipped(versions, steps, output = console.log) {
168
+ output(chalk.yellow(' Project files were created without installing dependencies.'))
169
+ output(chalk.gray(detectedLine(versions)))
170
+ for (const step of steps) output(chalk.gray(` Retry with: ${step.retry}`))
171
+ }
172
+
173
+ export function printNextSteps(answers, stack, output = console.log) {
174
+ output('')
175
+ output(chalk.bold(' Next steps:'))
176
+ output(chalk.gray(` cd ${answers.projectName}`))
177
+ if (stack.backendKey === 'laravel') {
178
+ const laravelDir = ['laravel-ui', 'no-frontend'].includes(stack.frontendKey) ? '' : 'backend/'
179
+ output(chalk.gray(` cp ${laravelDir}.env.example ${laravelDir}.env`))
180
+ if (answers.makefile) {
181
+ output(chalk.gray(' make setup # first run only'))
182
+ output(chalk.gray(' make run # later runs; never rebuilds'))
183
+ } else {
184
+ output(chalk.gray(' docker compose build'))
185
+ output(chalk.gray(' docker compose up -d'))
186
+ }
187
+ } else if (stack.frontendKey === 'react') {
188
+ if (stack.backendKey === 'springboot') output(chalk.gray(' cp .env.example .env # Docker/backend values'))
189
+ output(chalk.gray(' cd frontend'))
190
+ output(chalk.gray(' cp .env.example .env'))
191
+ if (!answers.installDependencies) output(chalk.gray(' npm install'))
192
+ } else {
193
+ output(chalk.gray(` cp .env.example ${stack.isMobile ? '.env' : '.env.local'}`))
194
+ if (!answers.installDependencies) output(chalk.gray(' npm install'))
195
+ }
196
+ if (stack.backendKey !== 'laravel') {
197
+ if (stack.isMobile) output(chalk.gray(' npx expo start'))
198
+ else if (answers.makefile) {
199
+ if (stack.frontendKey === 'react') output(chalk.gray(' cd ..'))
200
+ output(chalk.gray(' make dev'))
201
+ } else if (stack.backendKey === 'supabase') {
202
+ output(chalk.gray(' npm run supabase:start'))
203
+ output(chalk.gray(' npm run dev'))
204
+ } else if (stack.frontendKey === 'react') output(chalk.gray(' npm run dev'))
205
+ else {
206
+ output(chalk.gray(' docker compose up -d db'))
207
+ output(chalk.gray(' npm run dev'))
208
+ }
209
+ }
210
+ output('')
211
+ output(chalk.cyan(' Read RULES.md before starting — it maps every playbook for this stack.'))
212
+ output('')
213
+ }
214
+
215
+ export function printSupportPrompt(output = console.log) {
216
+ output(chalk.gray(' ───────────────────────────'))
217
+ output(chalk.gray(' Enjoyed this starter? ⭐ Star https://github.com/itsw1n/create-win-project'))
218
+ output(chalk.gray(' Found a gap or idea? Open an issue: https://github.com/itsw1n/create-win-project/issues/new'))
219
+ output('')
220
+ }
@@ -0,0 +1,185 @@
1
+ import inquirer from 'inquirer'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+ import { generateProject } from '../engine/create-project.js'
5
+ import { runDependencySteps } from '../engine/install-dependencies.js'
6
+ import { loadCompatibility } from '../engine/tested-versions.js'
7
+ import { buildQuestions, configurationDecisionChoices, lastEnabledIndex, promptWithBack } from './questions.js'
8
+ import {
9
+ printBanner,
10
+ generationFailed,
11
+ installFailed,
12
+ installSucceeded,
13
+ printInstallFailure,
14
+ printInstallSkipped,
15
+ printLocationNotice,
16
+ printNextSteps,
17
+ printSupportPrompt,
18
+ printCancelled,
19
+ printRuntimeInstructions,
20
+ printRuntimeMismatch,
21
+ printRuntimeSkip,
22
+ printSummary,
23
+ projectSucceeded,
24
+ projectLocationNotice,
25
+ startInstallSpinner,
26
+ startProjectSpinner,
27
+ } from './display.js'
28
+ import { parseArguments } from './arguments.js'
29
+ import {
30
+ loadCatalog, resolveStack,
31
+ } from '../engine/load-library.js'
32
+ import {
33
+ decideInstallation,
34
+ detectSystemVersions,
35
+ installationIssues,
36
+ runtimeSetupInstructions,
37
+ printDoctor,
38
+ } from './system-check.js'
39
+
40
+ const cliDirectory = path.dirname(fileURLToPath(import.meta.url))
41
+ const projectRoot = path.resolve(cliDirectory, '../..')
42
+
43
+ export async function runCli() {
44
+ const args = parseArguments(process.argv.slice(2))
45
+ const profileArg = args.profile
46
+ const shapeArg = args.shape
47
+ const frontendArg = args.frontend
48
+ const backendArg = args.backend
49
+ const architectureArg = args.architecture
50
+ const authenticationArg = args.authentication
51
+ const authAudienceArg = args.authAudience
52
+ const laravelUiArg = args.laravelUi
53
+ const wantsInstall = args.install
54
+ const skipsInstall = args.noInstall
55
+ const { profile } = await loadCompatibility(
56
+ path.join(projectRoot, 'library/tested-versions.json'),
57
+ profileArg,
58
+ )
59
+ if (args.doctor) {
60
+ printDoctor(profile)
61
+ process.exit(0)
62
+ }
63
+ const catalog = await loadCatalog(path.join(projectRoot, 'library'), profile)
64
+
65
+ // ─── Banner ──────────────────────────────────────────────────────────────────
66
+
67
+ printBanner(profile)
68
+
69
+ // ─── Interview ───────────────────────────────────────────────────────────────
70
+
71
+ const questions = buildQuestions({ args, catalog })
72
+ let answers = {
73
+ applicationShape: shapeArg,
74
+ frontend: frontendArg,
75
+ backend: backendArg,
76
+ }
77
+ let stack
78
+ let resumeAt = 0
79
+ while (true) {
80
+ answers = await promptWithBack(inquirer, questions, answers, resumeAt)
81
+ resumeAt = 0
82
+ answers.compatibilityProfile = profile.id
83
+ answers.applicationShape = shapeArg || answers.applicationShape
84
+ answers.architecture = architectureArg || answers.architecture || 'medium'
85
+ answers.authentication = authenticationArg || answers.authentication || 'not-yet'
86
+ answers.authAudience = authAudienceArg || answers.authAudience || (catalog.byId[answers.frontend]?.platform === 'mobile' ? 'multi-client' : 'website')
87
+ answers.laravelUi = laravelUiArg || answers.laravelUi || (answers.frontend === 'laravel-ui' ? 'blade' : undefined)
88
+ if (wantsInstall) answers.installDependencies = true
89
+ if (skipsInstall) answers.installDependencies = false
90
+
91
+ stack = resolveStack({ ...answers, styling: answers.styling || catalog.byId[answers.frontend]?.stylingOptions?.[0] || 'tailwind' }, catalog)
92
+
93
+ // ─── Confirm ─────────────────────────────────────────────────────────────────
94
+
95
+ printSummary({ answers, stack, catalog })
96
+
97
+ const { decision } = await inquirer.prompt([{
98
+ type: 'list', name: 'decision', message: 'Ready?', choices: configurationDecisionChoices(),
99
+ }])
100
+
101
+ if (decision === 'back') {
102
+ resumeAt = lastEnabledIndex(questions, answers)
103
+ continue
104
+ }
105
+ if (decision === 'cancel') {
106
+ printCancelled()
107
+ process.exit(0)
108
+ }
109
+ break
110
+ }
111
+
112
+ const detectedVersions = detectSystemVersions()
113
+ const runtimeIssues = installationIssues(stack, profile, detectedVersions)
114
+ const installationDecision = await decideInstallation({
115
+ requested: answers.installDependencies,
116
+ issues: runtimeIssues,
117
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
118
+ choose: async () => {
119
+ printRuntimeMismatch(runtimeIssues)
120
+ const { runtimeDecision } = await inquirer.prompt([{
121
+ type: 'list',
122
+ name: 'runtimeDecision',
123
+ message: 'How should create-win-project continue?',
124
+ choices: [
125
+ { name: 'Create files and skip dependency installation', value: 'skip' },
126
+ { name: 'Show runtime setup instructions', value: 'instructions' },
127
+ { name: 'Cancel', value: 'cancel' },
128
+ ],
129
+ }])
130
+ return runtimeDecision
131
+ },
132
+ })
133
+
134
+ if (installationDecision.reason === 'cancelled') {
135
+ printCancelled({ explainNoFiles: true })
136
+ process.exit(0)
137
+ }
138
+ if (installationDecision.reason === 'show-instructions') {
139
+ printRuntimeInstructions(runtimeSetupInstructions(profile, runtimeIssues))
140
+ process.exit(0)
141
+ }
142
+ if (answers.installDependencies && !installationDecision.install) {
143
+ printRuntimeSkip(runtimeIssues)
144
+ }
145
+ answers.installDependencies = installationDecision.install
146
+
147
+ // ─── Generate ────────────────────────────────────────────────────────────────
148
+
149
+ const spinner = startProjectSpinner()
150
+
151
+ try {
152
+ await generateProject(answers, projectRoot)
153
+ projectSucceeded(spinner)
154
+ const generatedProjectRoot = path.join(process.cwd(), answers.projectName)
155
+ const steps = []
156
+ if (stack.backendKey === 'laravel') {
157
+ const laravelRoot = ['laravel-ui', 'no-frontend'].includes(stack.frontendKey) ? generatedProjectRoot : path.join(generatedProjectRoot, 'backend')
158
+ steps.push({ command: process.platform === 'win32' ? 'composer.bat' : 'composer', args: ['install'], cwd: laravelRoot, retry: `cd ${path.relative(process.cwd(), laravelRoot)} && composer install` })
159
+ }
160
+ const needsNpm = stack.frontendKey !== 'no-frontend' && (stack.frontendKey !== 'laravel-ui' || answers.laravelUi === 'inertia-react')
161
+ if (needsNpm) {
162
+ const npmRoot = stack.frontendKey === 'react' ? path.join(generatedProjectRoot, 'frontend') : generatedProjectRoot
163
+ steps.push({ command: process.platform === 'win32' ? 'npm.cmd' : 'npm', args: ['install'], cwd: npmRoot, retry: `cd ${path.relative(process.cwd(), npmRoot)} && npm install` })
164
+ }
165
+ const locationNotice = projectLocationNotice({ cwd: process.cwd(), cliRoot: projectRoot, projectName: answers.projectName })
166
+ printLocationNotice(locationNotice)
167
+ if (answers.installDependencies) {
168
+ const installSpinner = startInstallSpinner()
169
+ const { failed } = runDependencySteps(steps)
170
+ if (failed) {
171
+ installFailed(installSpinner)
172
+ printInstallFailure(detectedVersions, failed.retry)
173
+ } else {
174
+ installSucceeded(installSpinner)
175
+ }
176
+ } else if (installationDecision.reason === 'runtime-mismatch') {
177
+ printInstallSkipped(detectedVersions, steps)
178
+ }
179
+ printNextSteps(answers, stack)
180
+ if (process.stdout.isTTY) printSupportPrompt()
181
+ } catch (err) {
182
+ generationFailed(spinner, err)
183
+ process.exit(1)
184
+ }
185
+ }