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
@@ -0,0 +1,7 @@
1
+ import { partitionEnvironment, renderEnvironment } from '../../shared/environment.js'
2
+
3
+ export function buildEnvironmentFiles(answers, stack) {
4
+ const { publicNames, serverNames } = partitionEnvironment(stack)
5
+ const names = stack.backendKey === 'springboot' ? [...publicNames, ...serverNames] : publicNames
6
+ return { '.env.example': renderEnvironment(names, answers) }
7
+ }
@@ -0,0 +1,30 @@
1
+ import { defineStackAdapter } from '../../rules.js'
2
+ import { ciContributions } from './ci.js'
3
+ import { dockerContributions } from './docker.js'
4
+ import { buildReactNativeFiles } from './create-files.js'
5
+
6
+ export const reactNativeAdapter = defineStackAdapter({
7
+ id: 'react-native',
8
+ kind: 'frontend',
9
+ label: 'React Native (Expo)',
10
+ compatibleWith: { backend: ['none', 'supabase', 'springboot', 'laravel'] },
11
+ capabilities: {
12
+ applicationShapes: ['mobile'],
13
+ architectureProfiles: ['small', 'medium', 'large'],
14
+ authenticationModels: ['public', 'undecided', 'supabase', 'oidc', 'laravel-oidc'],
15
+ runtime: 'node',
16
+ },
17
+ contributes: {
18
+ files: ({ answers, stack, shared }) => Object.entries(buildReactNativeFiles(answers, stack, shared)),
19
+ environment: ({ backend }) => backend.id === 'none' ? [] : ['API_URL'],
20
+ install: () => [{ cwd: '.', command: 'npm', args: ['install'] }],
21
+ docker: dockerContributions,
22
+ ci: ciContributions,
23
+ verification: () => [
24
+ { backend: 'none', architecture: 'small', authentication: 'public' },
25
+ { backend: 'supabase', architecture: 'medium', authentication: 'supabase' },
26
+ { backend: 'springboot', architecture: 'large', authentication: 'oidc' },
27
+ { backend: 'laravel', architecture: 'medium', authentication: 'laravel-oidc' },
28
+ ],
29
+ },
30
+ })
@@ -0,0 +1,3 @@
1
+ export function ciContributions() {
2
+ return [{ template: 'vite', path: '.github/workflows/ci-frontend.yml' }]
3
+ }
@@ -0,0 +1,27 @@
1
+ import { html, json } from '../../shared/javascript-package.js'
2
+ import { addTestingFiles } from '../../shared/testing-files.js'
3
+ import { packageFile } from './dependencies.js'
4
+
5
+ export function buildReactViteFiles(answers, stack, shared) {
6
+ const root = 'frontend'
7
+ const files = {}
8
+ files[`${root}/package.json`] = packageFile(answers, stack)
9
+ files[`${root}/.node-version`] = `${stack.profile.runtimes.node}\n`
10
+ files[`${root}/.npmrc`] = 'engine-strict=true\n'
11
+ files[`${root}/tsconfig.json`] = json({ compilerOptions: { target: 'ES2022', useDefineForClassFields: true, lib: ['ES2022', 'DOM', 'DOM.Iterable'], allowJs: false, skipLibCheck: true, esModuleInterop: true, allowSyntheticDefaultImports: true, strict: true, forceConsistentCasingInFileNames: true, module: 'ESNext', moduleResolution: 'Bundler', resolveJsonModule: true, isolatedModules: true, noEmit: true, jsx: 'react-jsx', paths: { '@/*': ['./src/*'] } }, include: ['src', 'vite.config.ts', 'vitest.config.ts'] })
12
+ files[`${root}/index.html`] = `<!doctype html>\n<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="description" content="${html(answers.projectDescription)}" /><title>${html(answers.projectName)}</title></head><body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body></html>\n`
13
+ files[`${root}/vite.config.ts`] = `import { fileURLToPath, URL } from 'node:url'\nimport { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n${stack.styleId === 'tailwind' ? "import tailwindcss from '@tailwindcss/vite'\n" : ''}\nexport default defineConfig({\n plugins: [react()${stack.styleId === 'tailwind' ? ', tailwindcss()' : ''}],\n resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } },\n})\n`
14
+ files[`${root}/eslint.config.js`] = `import js from '@eslint/js'\nimport tseslint from 'typescript-eslint'\n\nexport default tseslint.config(\n { ignores: ['dist', 'coverage', 'playwright-report'] },\n js.configs.recommended,\n ...tseslint.configs.recommended,\n { files: ['**/*.{ts,tsx}'], languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },\n)\n`
15
+ files[`${root}/src/vite-env.d.ts`] = "/// <reference types=\"vite/client\" />\n"
16
+ files[`${root}/src/main.tsx`] = `import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { App } from './App'\nimport './styles.css'\n\ncreateRoot(document.getElementById('root')!).render(<StrictMode><App /></StrictMode>)\n`
17
+ files[`${root}/src/App.tsx`] = stack.architecture === 'small'
18
+ ? `export function App() {\n return <main><p>create-win-project</p><h1>Your starter is running</h1><p>{${JSON.stringify(answers.projectDescription)}}</p><p>Read <code>AGENTS.md</code> before your first agent-assisted change.</p></main>\n}\n`
19
+ : `${stack.architecture === 'large'
20
+ ? "import { getStarterStatus, StarterStatus } from '@/features/status'"
21
+ : "import { StarterStatus } from '@/features/status/components/StarterStatus'\nimport { getStarterStatus } from '@/features/status/services/getStarterStatus'"}\n\nexport function App() {\n const status = getStarterStatus()\n return <main><p>create-win-project</p><StarterStatus status={status} /><p>{${JSON.stringify(answers.projectDescription)}}</p><p>Read <code>AGENTS.md</code> before your first agent-assisted change.</p></main>\n}\n`
22
+ files[`${root}/src/styles.css`] = `${stack.styleId === 'tailwind' ? '@import "tailwindcss";\n' : ''}:root { color-scheme: light dark; font-family: system-ui, sans-serif; }\n* { box-sizing: border-box; }\nbody { margin: 0; }\nmain { max-width: 48rem; margin: 0 auto; padding: 4rem 1.5rem; }\n`
23
+ if ((answers.testing || 'basic') !== 'none') files[`${root}/src/App.test.tsx`] = `import { expect, test } from 'vitest'\nimport { render, screen } from '@testing-library/react'\nimport { App } from './App'\n\ntest('renders the starter heading', () => {\n render(<App />)\n expect(screen.getByRole('heading', { name: 'Your starter is running' })).toBeInTheDocument()\n})\n`
24
+ addTestingFiles(files, root, stack, answers.testing || 'basic')
25
+ Object.assign(files, shared.statusFeatureFiles(root, stack))
26
+ return files
27
+ }
@@ -0,0 +1,5 @@
1
+ import { buildJavaScriptPackage } from '../../shared/javascript-package.js'
2
+
3
+ export function packageFile(answers, stack) {
4
+ return buildJavaScriptPackage(answers, stack, 'react')
5
+ }
@@ -0,0 +1,3 @@
1
+ export function dockerContributions() {
2
+ return [{ template: 'vite', root: 'frontend', developmentPath: 'Dockerfile.dev', productionPath: 'Dockerfile' }]
3
+ }
@@ -0,0 +1,8 @@
1
+ import { partitionEnvironment, renderEnvironment } from '../../shared/environment.js'
2
+
3
+ export function buildEnvironmentFiles(answers, stack) {
4
+ const { publicNames, serverNames } = partitionEnvironment(stack)
5
+ const files = { 'frontend/.env.example': renderEnvironment(publicNames, answers) }
6
+ if (stack.backendKey === 'springboot') files['.env.example'] = renderEnvironment(serverNames, answers)
7
+ return files
8
+ }
@@ -0,0 +1,32 @@
1
+ import { defineStackAdapter } from '../../rules.js'
2
+ import { ciContributions } from './ci.js'
3
+ import { dockerContributions } from './docker.js'
4
+ import { buildReactViteFiles } from './create-files.js'
5
+
6
+ const verificationCases = Object.freeze([
7
+ Object.freeze({ backend: 'none', styling: 'css-modules', architecture: 'small', authentication: 'public' }),
8
+ Object.freeze({ backend: 'supabase', styling: 'tailwind', architecture: 'medium', authentication: 'supabase' }),
9
+ Object.freeze({ backend: 'springboot', styling: 'css-modules', architecture: 'large', authentication: 'session' }),
10
+ Object.freeze({ backend: 'laravel', styling: 'tailwind', architecture: 'medium', authentication: 'sanctum-spa' }),
11
+ ])
12
+
13
+ export const reactViteAdapter = defineStackAdapter({
14
+ id: 'react',
15
+ kind: 'frontend',
16
+ label: 'React + Vite',
17
+ compatibleWith: { backend: ['none', 'supabase', 'springboot', 'laravel'] },
18
+ capabilities: {
19
+ applicationShapes: ['separate', 'frontend'],
20
+ architectureProfiles: ['small', 'medium', 'large'],
21
+ authenticationModels: ['public', 'undecided', 'supabase', 'session', 'oidc', 'sanctum-spa', 'laravel-oidc'],
22
+ runtime: 'node',
23
+ },
24
+ contributes: {
25
+ files: ({ answers, stack, shared }) => Object.entries(buildReactViteFiles(answers, stack, shared)),
26
+ environment: ({ backend }) => backend.id === 'none' ? [] : ['API_URL'],
27
+ install: () => [{ cwd: 'frontend', command: 'npm', args: ['install'] }],
28
+ docker: dockerContributions,
29
+ ci: ciContributions,
30
+ verification: () => verificationCases,
31
+ },
32
+ })
@@ -1,4 +1,4 @@
1
- import { CONTRIBUTION_HOOKS, PROMPT_SLOTS } from '../contract.js'
1
+ import { CONTRIBUTION_HOOKS, PROMPT_SLOTS } from '../rules.js'
2
2
 
3
3
  export function collectContributions(adapters, hook, context) {
4
4
  if (!CONTRIBUTION_HOOKS.includes(hook)) throw new Error(`Unknown contribution hook: ${hook}`)
@@ -0,0 +1,30 @@
1
+ export const environmentHeader = '# Copy to .env or .env.local. Never commit real credentials.\n\n'
2
+
3
+ export function environmentLine(name, answers) {
4
+ const defaults = {
5
+ NEXT_PUBLIC_API_URL: 'http://localhost:8080',
6
+ VITE_API_URL: 'http://localhost:8080',
7
+ EXPO_PUBLIC_API_URL: 'http://localhost:8080',
8
+ DATABASE_URL: `jdbc:postgresql://localhost:5432/${answers.projectName.replaceAll('-', '_')}`,
9
+ POSTGRES_USER: 'postgres',
10
+ POSTGRES_PASSWORD: 'change-me',
11
+ POSTGRES_DB: answers.projectName.replaceAll('-', '_'),
12
+ SPRING_PROFILES_ACTIVE: 'dev',
13
+ SPRING_SECURITY_USER_NAME: 'developer',
14
+ SPRING_SECURITY_USER_PASSWORD: 'change-me-before-production',
15
+ OIDC_ISSUER_URI: 'http://localhost:9090/realms/app',
16
+ OIDC_AUDIENCE: 'api',
17
+ }
18
+ return `${name}=${defaults[name] || ''}`
19
+ }
20
+
21
+ export function renderEnvironment(names, answers) {
22
+ return `${environmentHeader}${names.map((name) => environmentLine(name, answers)).join('\n')}\n`
23
+ }
24
+
25
+ export function partitionEnvironment(stack) {
26
+ return {
27
+ publicNames: stack.env.filter((name) => name.startsWith(stack.envPrefix)),
28
+ serverNames: stack.env.filter((name) => !name.startsWith(stack.envPrefix)),
29
+ }
30
+ }
@@ -0,0 +1,111 @@
1
+ export function packageVersion(profile, name, capability) {
2
+ const configured = profile.packages[name]
3
+ const resolved = typeof configured === 'string'
4
+ ? configured
5
+ : configured?.overrides?.[capability] || configured?.default
6
+ if (!resolved) throw new Error(`${capability} requires ${name} in compatibility profile ${profile.id}`)
7
+ return resolved
8
+ }
9
+
10
+ export function composerPackageVersion(profile, name, owner = name) {
11
+ const resolved = profile.composerPackages?.[name]
12
+ if (!resolved) throw new Error(`${owner} requires ${name} in compatibility profile ${profile.id}`)
13
+ return resolved
14
+ }
15
+
16
+ export function json(value) {
17
+ return `${JSON.stringify(value, null, 2)}\n`
18
+ }
19
+
20
+ export function html(value) {
21
+ return String(value).replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
22
+ }
23
+
24
+ export function buildJavaScriptPackage(answers, stack, owner) {
25
+ const level = answers.testing || 'basic'
26
+ const scripts = { ...stack.scripts, start: owner === 'nextjs' ? 'next start' : undefined }
27
+ const devDependencies = { ...stack.devDeps }
28
+
29
+ if (level === 'none') delete scripts.test
30
+ if (level !== 'none') {
31
+ if (owner === 'react-native') {
32
+ Object.assign(devDependencies, {
33
+ jest: packageVersion(stack.profile, 'jest', owner),
34
+ '@types/jest': packageVersion(stack.profile, '@types/jest', owner),
35
+ 'jest-expo': packageVersion(stack.profile, 'jest-expo', owner),
36
+ '@testing-library/react-native': packageVersion(stack.profile, '@testing-library/react-native', owner),
37
+ })
38
+ } else {
39
+ Object.assign(devDependencies, {
40
+ vitest: packageVersion(stack.profile, 'vitest', owner),
41
+ jsdom: packageVersion(stack.profile, 'jsdom', owner),
42
+ '@testing-library/react': packageVersion(stack.profile, '@testing-library/react', owner),
43
+ '@testing-library/jest-dom': packageVersion(stack.profile, '@testing-library/jest-dom', owner),
44
+ })
45
+ }
46
+ }
47
+ if (level === 'full' && owner !== 'react-native') {
48
+ scripts['test:e2e'] = 'playwright test'
49
+ devDependencies['@playwright/test'] = packageVersion(stack.profile, '@playwright/test', owner)
50
+ }
51
+
52
+ scripts.typecheck = 'tsc --noEmit'
53
+ scripts.format = 'prettier --write .'
54
+ scripts['format:check'] = 'prettier --check .'
55
+ if (owner !== 'react-native') scripts.lint = 'eslint .'
56
+ if (stack.architecture === 'large') scripts['check:boundaries'] = 'node scripts/check-boundaries.mjs'
57
+ if (stack.backendKey === 'supabase') {
58
+ const workdir = owner === 'react' ? ' --workdir ..' : ''
59
+ scripts['supabase:start'] = `supabase${workdir} start`
60
+ scripts['supabase:stop'] = `supabase${workdir} stop`
61
+ scripts['supabase:status'] = `supabase${workdir} status`
62
+ scripts['supabase:reset'] = `supabase${workdir} db reset`
63
+ scripts['supabase:test'] = `supabase${workdir} test db`
64
+ const typePath = owner === 'react-native' ? 'types/database.types.ts' : 'src/types/database.types.ts'
65
+ scripts['supabase:types'] = `supabase${workdir} gen types typescript --local > ${typePath}`
66
+ }
67
+ if (stack.backendKey === 'postgres') {
68
+ scripts['prisma:generate'] = 'prisma generate'
69
+ scripts['db:migrate'] = 'prisma migrate dev'
70
+ scripts['db:deploy'] = 'prisma migrate deploy'
71
+ scripts['db:reset'] = 'prisma migrate reset --force'
72
+ scripts['db:studio'] = 'prisma studio'
73
+ scripts.dev = 'prisma generate && next dev'
74
+ scripts.build = 'prisma generate && next build'
75
+ scripts.typecheck = 'prisma generate && tsc --noEmit'
76
+ }
77
+ const boundary = stack.architecture === 'large' ? ' && npm run check:boundaries' : ''
78
+ scripts.check = owner === 'react-native'
79
+ ? `npm run format:check && npm run typecheck${level === 'none' ? '' : ' && npm test -- --runInBand'}${boundary}`
80
+ : `npm run format:check && npm run lint && npm run typecheck${level === 'none' ? '' : ' && npm test'}${boundary}`
81
+ Object.keys(scripts).forEach((key) => scripts[key] === undefined && delete scripts[key])
82
+
83
+ if (stack.styleId === 'tailwind') {
84
+ devDependencies.tailwindcss = packageVersion(stack.profile, 'tailwindcss', owner)
85
+ if (owner === 'nextjs') devDependencies['@tailwindcss/postcss'] = packageVersion(stack.profile, '@tailwindcss/postcss', owner)
86
+ if (owner === 'react') devDependencies['@tailwindcss/vite'] = packageVersion(stack.profile, '@tailwindcss/vite', owner)
87
+ }
88
+ const dependencies = { ...stack.deps }
89
+ if (owner !== 'nextjs') delete dependencies['@supabase/ssr']
90
+ if (owner === 'react-native' && stack.authentication === 'supabase') {
91
+ dependencies['expo-secure-store'] = packageVersion(stack.profile, 'expo-secure-store', owner)
92
+ }
93
+
94
+ const packageJson = {
95
+ name: answers.projectName,
96
+ version: '0.1.0',
97
+ private: true,
98
+ type: 'module',
99
+ packageManager: `npm@${stack.profile.runtimes.npmMinimum}`,
100
+ engines: {
101
+ node: `>=${stack.profile.runtimes.nodeMinimum}`,
102
+ npm: `>=${stack.profile.runtimes.npmMinimum}`,
103
+ },
104
+ main: owner === 'react-native' ? 'expo-router/entry' : undefined,
105
+ scripts,
106
+ dependencies,
107
+ devDependencies,
108
+ }
109
+ if (!packageJson.main) delete packageJson.main
110
+ return json(packageJson)
111
+ }
@@ -0,0 +1,16 @@
1
+ import path from 'node:path'
2
+
3
+ export function addTestingFiles(files, root, stack, level) {
4
+ if (level === 'none') return
5
+ if (stack.isMobile) {
6
+ files[path.join(root, 'jest.config.js')] = "export default { preset: 'jest-expo' }\n"
7
+ files[path.join(root, 'app/index.test.tsx')] = `import { render } from '@testing-library/react-native'\nimport HomeScreen from './index'\n\ntest('renders the starter heading', async () => {\n const view = await render(<HomeScreen />)\n expect(view.getByText('Your starter is running')).toBeTruthy()\n})\n`
8
+ return
9
+ }
10
+ files[path.join(root, 'vitest.config.ts')] = `import { fileURLToPath, URL } from 'node:url'\nimport { defineConfig } from 'vitest/config'\n\nexport default defineConfig({\n resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } },\n test: { environment: 'jsdom', setupFiles: './src/test/setup.ts', exclude: ['e2e/**', 'node_modules/**'] },\n})\n`
11
+ files[path.join(root, 'src/test/setup.ts')] = "import '@testing-library/jest-dom/vitest'\n"
12
+ if (level === 'full') {
13
+ files[path.join(root, 'playwright.config.ts')] = `import { defineConfig, devices } from '@playwright/test'\n\nexport default defineConfig({\n testDir: './e2e',\n use: { baseURL: 'http://127.0.0.1:${stack.frontendPort}' },\n webServer: { command: 'npm run dev -- ${stack.frontendKey === 'nextjs' ? '--hostname' : '--host'} 127.0.0.1 --port ${stack.frontendPort}', url: 'http://127.0.0.1:${stack.frontendPort}', reuseExistingServer: true, timeout: 120_000 },\n projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],\n})\n`
14
+ files[path.join(root, 'e2e/home.spec.ts')] = `import { expect, test } from '@playwright/test'\n\ntest('loads the starter', async ({ page }) => {\n await page.goto('/')\n await expect(page.getByRole('heading', { name: 'Your starter is running' })).toBeVisible()\n})\n`
15
+ }
16
+ }
@@ -5,7 +5,7 @@ services:
5
5
  container_name: {{PROJECT_NAME}}-db
6
6
  image: {{POSTGRES_IMAGE}}
7
7
  ports:
8
- - "5432:5432"
8
+ - "${POSTGRES_HOST_PORT:-5432}:5432"
9
9
  environment:
10
10
  - POSTGRES_USER=${POSTGRES_USER}
11
11
  - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
@@ -7,7 +7,7 @@ services:
7
7
  context: {{FRONTEND_DIR}}
8
8
  dockerfile: {{FRONTEND_DOCKERFILE}}
9
9
  ports:
10
- - "{{FRONTEND_PORT}}:{{FRONTEND_PORT}}"
10
+ - "${FRONTEND_HOST_PORT:-{{FRONTEND_PORT}}}:{{FRONTEND_PORT}}"
11
11
  volumes:
12
12
  - ./{{FRONTEND_DIR}}:/app
13
13
  - /app/node_modules
@@ -24,7 +24,7 @@ services:
24
24
  context: ./backend
25
25
  dockerfile: Dockerfile.dev
26
26
  ports:
27
- - "8080:8080"
27
+ - "${BACKEND_HOST_PORT:-8080}:8080"
28
28
  volumes:
29
29
  - ./backend:/app
30
30
  - maven-cache:/root/.m2
@@ -43,7 +43,7 @@ services:
43
43
  container_name: {{PROJECT_NAME}}-db
44
44
  image: {{POSTGRES_IMAGE}}
45
45
  ports:
46
- - "5432:5432"
46
+ - "${POSTGRES_HOST_PORT:-5432}:5432"
47
47
  environment:
48
48
  - POSTGRES_USER=${POSTGRES_USER}
49
49
  - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
@@ -6,7 +6,7 @@ services:
6
6
  context: {{FRONTEND_DIR}}
7
7
  dockerfile: {{FRONTEND_DOCKERFILE}}
8
8
  ports:
9
- - "{{FRONTEND_PORT}}:{{FRONTEND_PORT}}"
9
+ - "${FRONTEND_HOST_PORT:-{{FRONTEND_PORT}}}:{{FRONTEND_PORT}}"
10
10
  volumes:
11
11
  - ./{{FRONTEND_DIR}}:/app
12
12
  - /app/node_modules
@@ -24,7 +24,7 @@ services:
24
24
  dockerfile: Dockerfile
25
25
  target: production
26
26
  ports:
27
- - "8080:8080"
27
+ - "${BACKEND_HOST_PORT:-8080}:8080"
28
28
  environment:
29
29
  - SPRING_PROFILES_ACTIVE=prod
30
30
  - DATABASE_URL=jdbc:postgresql://db:5432/${POSTGRES_DB}
package/lib/banner.js DELETED
@@ -1,45 +0,0 @@
1
- import chalk from 'chalk'
2
-
3
- const W1N = [
4
- ' ██╗ ██╗ ██╗ ███╗ ██╗',
5
- ' ██║ ██║ ███║ ████╗ ██║',
6
- ' ██║ █╗ ██║ ╚██║ ██╔██╗██║',
7
- ' ██║███╗██║ ██║ ██║╚████║',
8
- ' ╚███╔███╔╝ ██║ ██║ ╚███║',
9
- ' ╚══╝╚══╝ ╚═╝ ╚═╝ ╚══╝',
10
- ]
11
-
12
- const PROJECT = [
13
- ' █▀█ █▀█ █▀█ █ █▀▀ █▀▀ ▀█▀',
14
- ' █▀▀ █▀█ █ █ █ █▀ █ █ ',
15
- ' █ █ █ █▄█ █▄█ █▄▄ █▄▄ █ ',
16
- ]
17
-
18
- function plain() {
19
- return [...W1N, '', ...PROJECT].join('\n')
20
- }
21
-
22
- function colored() {
23
- const w1n = W1N.map((l) => chalk.bold.cyan(l)).join('\n')
24
- const proj = PROJECT.map((l) => chalk.bold.white(l)).join('\n')
25
- return `${w1n}\n${proj}`
26
- }
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
-
33
- // Fallback for narrow terminals or non-TTY (CI/logs): plain single-line wordmark
34
- if (!isTTY || width < 80 || noColor) {
35
- // Keep stacked plain for README consistency, but without colors
36
- if (width < 60) return 'W1N PROJECT'
37
- return plain()
38
- }
39
-
40
- return colored()
41
- }
42
-
43
- export const bannerRaw = plain()
44
- export const W1N_ART = W1N.join('\n')
45
- export const PROJECT_ART = PROJECT.join('\n')
package/lib/constants.js DELETED
@@ -1,3 +0,0 @@
1
- // Deprecated: stack composition now lives in playbooks/*.manifest.json + lib/catalog.js.
2
- // Kept as an empty module so any stray import does not break resolution.
3
- export {}
package/lib/doctor.js DELETED
@@ -1,45 +0,0 @@
1
- import { spawnSync } from 'node:child_process'
2
- import { existsSync } from 'node:fs'
3
- import path from 'node:path'
4
-
5
- function isOnPath(command) {
6
- const suffixes = process.platform === 'win32' ? ['', '.cmd', '.exe', '.bat'] : ['']
7
- return (process.env.PATH || '').split(path.delimiter).some((directory) =>
8
- suffixes.some((suffix) => existsSync(path.join(directory, `${command}${suffix}`)))
9
- )
10
- }
11
-
12
- function probe(command, args = ['--version']) {
13
- const result = spawnSync(command, args, { encoding: 'utf8', shell: false })
14
- const version = `${result.stdout || result.stderr || ''}`.trim().split('\n')[0]
15
- if (!version) return isOnPath(command) ? 'available (version unavailable)' : null
16
- if (result.status !== 0) return null
17
- return version
18
- }
19
-
20
- export function collectDiagnostics(profile) {
21
- const nodeVersion = process.versions.node
22
- const npmVersion = probe('npm')
23
- const dockerVersion = probe('docker')
24
- const composeVersion = dockerVersion ? probe('docker', ['compose', 'version']) : null
25
- const javaVersion = probe('java', ['-version'])
26
- return [
27
- { name: 'Node.js', found: nodeVersion, expected: profile.runtimes.node, required: false },
28
- { name: 'npm', found: npmVersion, required: false },
29
- { name: 'Docker', found: dockerVersion, required: false },
30
- { name: 'Docker Compose', found: composeVersion, required: false },
31
- { name: 'Java', found: javaVersion, expected: profile.runtimes.java, required: false },
32
- ]
33
- }
34
-
35
- export function printDoctor(profile, output = console.log) {
36
- output(`create-win-project doctor (profile ${profile.id})`)
37
- output('Use either the Node/npm lane or the Docker/Compose lane; Java is only needed for host-run Spring projects.')
38
- for (const item of collectDiagnostics(profile)) {
39
- const expectation = item.expected ? ` (tested: ${item.expected})` : ''
40
- output(`${item.found ? '✓' : '○'} ${item.name}: ${item.found || 'not found'}${expectation}`)
41
- }
42
- output('')
43
- output('Node lane: npm ci && npm start')
44
- output('Docker lane: docker compose build && docker compose run --rm app')
45
- }
package/lib/interview.js DELETED
@@ -1,77 +0,0 @@
1
- const BACK = Symbol('back')
2
-
3
- function enabled(question, answers) {
4
- return typeof question.when === 'function' ? question.when(answers) : question.when !== false
5
- }
6
-
7
- async function resolved(value, answers) {
8
- return typeof value === 'function' ? value(answers) : value
9
- }
10
-
11
- function clearFrom(questions, answers, index) {
12
- for (let cursor = index; cursor < questions.length; cursor += 1) {
13
- delete answers[questions[cursor].name]
14
- }
15
- }
16
-
17
- /**
18
- * Runs Inquirer questions one at a time so users can safely revisit earlier answers.
19
- * List/checkbox/confirm prompts expose a visible Back choice; text prompts accept `:back`.
20
- */
21
- export async function promptWithBack(inquirer, questions, initialAnswers = {}) {
22
- const answers = { ...initialAnswers }
23
- const history = []
24
- let index = 0
25
-
26
- while (index < questions.length) {
27
- const source = questions[index]
28
- if (!enabled(source, answers)) { index += 1; continue }
29
-
30
- const canGoBack = history.length > 0
31
- const question = { ...source, when: undefined }
32
- if (answers[source.name] !== undefined && question.default === undefined) {
33
- question.default = answers[source.name]
34
- }
35
- if (question.type === 'input' && canGoBack) {
36
- question.message = `${question.message} (type :back to return)`
37
- } else if (question.type === 'confirm') {
38
- const defaultValue = await resolved(question.default, answers)
39
- question.type = 'list'
40
- question.choices = [
41
- { name: 'Yes', value: true },
42
- { name: 'No', value: false },
43
- ...(canGoBack ? [{ name: '← Back', value: BACK }] : []),
44
- ]
45
- question.default = defaultValue ? 0 : 1
46
- } else if (['list', 'checkbox'].includes(question.type) && canGoBack) {
47
- const choices = await resolved(question.choices, answers)
48
- question.choices = [...choices, new inquirer.Separator(), { name: '← Back', value: BACK }]
49
- }
50
-
51
- if (typeof question.default === 'function') question.default = await question.default(answers)
52
- const result = await inquirer.prompt([question], answers)
53
- const value = result[source.name]
54
- const requestedBack = value === BACK || value === ':back' || (Array.isArray(value) && value.includes(BACK))
55
- if (requestedBack) {
56
- const target = history.pop()
57
- if (target === undefined) continue
58
- clearFrom(questions, answers, target)
59
- index = target
60
- continue
61
- }
62
-
63
- answers[source.name] = value
64
- history.push(index)
65
- index += 1
66
- }
67
-
68
- return answers
69
- }
70
-
71
- export function configurationDecisionChoices() {
72
- return [
73
- { name: 'Create project', value: 'create' },
74
- { name: '← Back and edit', value: 'back' },
75
- { name: 'Cancel', value: 'cancel' },
76
- ]
77
- }
@@ -1,3 +0,0 @@
1
- // Compatibility export for programmatic consumers. Laravel generation now
2
- // lives behind its stack adapter boundary.
3
- export { buildLaravelFiles } from './stacks/laravel/generate.js'