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.
- package/README.md +19 -28
- package/{scripts/compatibility-matrix.mjs → checks/check-compatibility.js} +24 -6
- package/{scripts/verify-generated.mjs → checks/check-generated-project.js} +2 -2
- package/{scripts/validate-content.mjs → checks/check-library.js} +5 -5
- package/checks/classify-changes.js +39 -0
- package/docs/ARCHITECTURE.md +83 -40
- package/docs/CONTENT_MODEL.md +3 -3
- package/docs/CONTRIBUTING.md +154 -0
- package/docs/DEPENDENCY_MAINTENANCE.md +1 -1
- package/index.js +2 -429
- package/{playbooks → library}/INDEX.md +4 -4
- package/{playbooks/capabilities/ci/github-actions.manifest.json → library/development-tools/ci/definition.json} +1 -0
- package/{playbooks/devops/makefile.manifest.json → library/development-tools/devops/makefile/definition.json} +1 -0
- package/{playbooks/devops/pr-template.manifest.json → library/development-tools/devops/pr-template/definition.json} +1 -0
- package/{playbooks/platform/mobile.manifest.json → library/platforms/mobile/definition.json} +1 -0
- package/{playbooks/platform/web.manifest.json → library/platforms/web/definition.json} +1 -0
- package/{compatibility/profiles.json → library/tested-versions.json} +4 -0
- package/{playbooks/universal/accessibility.manifest.json → library/universal/accessibility/definition.json} +1 -1
- package/{playbooks/universal/coding-rules.manifest.json → library/universal/coding-rules/definition.json} +1 -0
- package/{playbooks/universal/error-handling.manifest.json → library/universal/error-handling/definition.json} +1 -0
- package/{playbooks/universal/git-conventions.manifest.json → library/universal/git-conventions/definition.json} +1 -0
- package/{playbooks/universal/observability.manifest.json → library/universal/observability/definition.json} +1 -0
- package/{playbooks/universal/security.manifest.json → library/universal/security/definition.json} +1 -0
- package/{playbooks/universal/typescript.manifest.json → library/universal/typescript/definition.json} +1 -0
- package/package.json +12 -11
- package/public/logo.svg +19 -0
- package/src/cli/arguments.js +52 -0
- package/src/cli/display.js +220 -0
- package/src/cli/main.js +185 -0
- package/src/cli/navigation.js +156 -0
- package/src/cli/questions.js +187 -0
- package/src/cli/system-check.js +138 -0
- package/src/engine/create-project.js +10 -0
- package/src/engine/install-dependencies.js +13 -0
- package/{lib/catalog.js → src/engine/load-library.js} +5 -6
- package/{lib/playbooks.js → src/engine/project-guidance.js} +25 -3
- package/{lib/template.js → src/engine/render-templates.js} +8 -0
- package/src/engine/write-files.js +34 -0
- package/{lib/stacks/registry.js → src/stacks/available-stacks.js} +23 -1
- package/src/stacks/backends/README.md +5 -0
- package/{lib/stacks → src/stacks/backends}/laravel/architecture.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/auth/oidc.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/auth/public.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/auth/sanctum.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/auth/session.js +1 -0
- package/src/stacks/backends/laravel/ci.js +3 -0
- package/{lib/stacks → src/stacks/backends}/laravel/composer.js +1 -1
- package/{lib/stacks/laravel/generate.js → src/stacks/backends/laravel/create-files.js} +1 -0
- package/src/stacks/backends/laravel/docker.js +14 -0
- package/src/stacks/backends/laravel/environment.js +5 -0
- package/{lib/stacks → src/stacks/backends}/laravel/index.js +11 -3
- package/{lib/stacks → src/stacks/backends}/laravel/ui/blade.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/ui/index.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/ui/inertia-react.js +4 -2
- package/{lib/stacks → src/stacks/backends}/laravel/ui/livewire.js +1 -0
- package/{lib/stacks → src/stacks/backends}/laravel/ui/shared.js +1 -0
- package/src/stacks/backends/none/index.js +23 -0
- package/src/stacks/backends/postgres/ci.js +3 -0
- package/src/stacks/backends/postgres/create-files.js +21 -0
- package/src/stacks/backends/postgres/docker.js +3 -0
- package/src/stacks/backends/postgres/environment.js +3 -0
- package/src/stacks/backends/postgres/index.js +30 -0
- package/src/stacks/backends/springboot/ci.js +3 -0
- package/src/stacks/backends/springboot/create-files.js +91 -0
- package/src/stacks/backends/springboot/docker.js +3 -0
- package/src/stacks/backends/springboot/environment.js +3 -0
- package/src/stacks/backends/springboot/index.js +31 -0
- package/src/stacks/backends/supabase/ci.js +3 -0
- package/src/stacks/backends/supabase/create-files.js +52 -0
- package/src/stacks/backends/supabase/docker.js +3 -0
- package/src/stacks/backends/supabase/environment.js +3 -0
- package/src/stacks/backends/supabase/index.js +31 -0
- package/src/stacks/backends/supabase/native.js +14 -0
- package/src/stacks/compose-files.js +241 -0
- package/{lib/generator.js → src/stacks/create-project.js} +78 -50
- package/src/stacks/frontends/README.md +4 -0
- package/src/stacks/frontends/nextjs/ci.js +3 -0
- package/src/stacks/frontends/nextjs/create-files.js +46 -0
- package/src/stacks/frontends/nextjs/dependencies.js +5 -0
- package/src/stacks/frontends/nextjs/docker.js +3 -0
- package/src/stacks/frontends/nextjs/environment.js +7 -0
- package/src/stacks/frontends/nextjs/index.js +35 -0
- package/src/stacks/frontends/react-native/ci.js +3 -0
- package/src/stacks/frontends/react-native/create-files.js +25 -0
- package/src/stacks/frontends/react-native/dependencies.js +5 -0
- package/src/stacks/frontends/react-native/docker.js +5 -0
- package/src/stacks/frontends/react-native/environment.js +7 -0
- package/src/stacks/frontends/react-native/index.js +30 -0
- package/src/stacks/frontends/react-vite/ci.js +3 -0
- package/src/stacks/frontends/react-vite/create-files.js +27 -0
- package/src/stacks/frontends/react-vite/dependencies.js +5 -0
- package/src/stacks/frontends/react-vite/docker.js +3 -0
- package/src/stacks/frontends/react-vite/environment.js +8 -0
- package/src/stacks/frontends/react-vite/index.js +32 -0
- package/{lib → src}/stacks/shared/contributions.js +1 -1
- package/src/stacks/shared/environment.js +30 -0
- package/src/stacks/shared/javascript-package.js +111 -0
- package/src/stacks/shared/testing-files.js +16 -0
- package/templates/docker/compose/postgres.yml +1 -1
- package/templates/docker/compose/springboot.yml +3 -3
- package/templates/docker/compose/supabase.yml +1 -1
- package/templates/docker/compose-prod/springboot.yml +1 -1
- package/lib/banner.js +0 -45
- package/lib/constants.js +0 -3
- package/lib/doctor.js +0 -45
- package/lib/interview.js +0 -77
- package/lib/laravel-scaffold.js +0 -3
- package/lib/scaffold.js +0 -467
- package/lib/stacks/index.js +0 -8
- /package/{playbooks/capabilities → library/development-tools}/ci/github-actions.md +0 -0
- /package/{playbooks/devops → library/development-tools/devops/makefile}/makefile.md +0 -0
- /package/{playbooks/devops → library/development-tools/devops/pr-template}/pr-template.md +0 -0
- /package/{playbooks/capabilities/docker/docker.manifest.json → library/development-tools/docker/definition.json} +0 -0
- /package/{playbooks/capabilities → library/development-tools}/docker/overview.md +0 -0
- /package/{playbooks/capabilities → library/features}/auth/oidc-resource-server.md +0 -0
- /package/{playbooks/capabilities → library/features}/auth/spring-session.md +0 -0
- /package/{playbooks/capabilities/flyway/flyway.manifest.json → library/features/flyway/definition.json} +0 -0
- /package/{playbooks/capabilities → library/features}/flyway/environments.md +0 -0
- /package/{playbooks/capabilities → library/features}/flyway/migrations.md +0 -0
- /package/{playbooks/capabilities → library/features}/flyway/testing.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/database.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/migrations.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/observability.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/oidc-resource-server.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/queues.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/sanctum-spa.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/scheduler.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/session-auth.md +0 -0
- /package/{playbooks/capabilities → library/features}/laravel/storage-uploads.md +0 -0
- /package/{playbooks/capabilities → library/features}/postgresql/architecture.md +0 -0
- /package/{playbooks/capabilities/postgresql/postgresql.manifest.json → library/features/postgresql/definition.json} +0 -0
- /package/{playbooks/capabilities → library/features}/postgresql/migrations.md +0 -0
- /package/{playbooks/capabilities → library/features}/postgresql/schema-design.md +0 -0
- /package/{playbooks/capabilities → library/features}/postgresql/security.md +0 -0
- /package/{playbooks/capabilities → library/features}/postgresql/testing.md +0 -0
- /package/{playbooks/capabilities → library/features}/prisma/architecture.md +0 -0
- /package/{playbooks/capabilities/prisma/prisma.manifest.json → library/features/prisma/definition.json} +0 -0
- /package/{playbooks/capabilities → library/features}/prisma/migrations.md +0 -0
- /package/{playbooks/capabilities → library/features}/prisma/runtime.md +0 -0
- /package/{playbooks/capabilities → library/features}/prisma/schema.md +0 -0
- /package/{playbooks/capabilities → library/features}/prisma/testing.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/architecture.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/authentication.md +0 -0
- /package/{playbooks/capabilities/supabase/supabase.manifest.json → library/features/supabase/definition.json} +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/expo.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/migrations.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/nextjs.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/rls.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/testing.md +0 -0
- /package/{playbooks/capabilities → library/features}/supabase/vite.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/axios.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/next-safe-action.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/next-themes.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/nuqs.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/t3-env.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/tanstack-query.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/zod.md +0 -0
- /package/{playbooks → library/optional-features}/concerns/zustand.md +0 -0
- /package/{playbooks/styling → library/optional-features/styling/css-modules}/css-modules-extensions.md +0 -0
- /package/{playbooks/styling/css-modules.manifest.json → library/optional-features/styling/css-modules/definition.json} +0 -0
- /package/{playbooks/styling/native-styles.manifest.json → library/optional-features/styling/native-styles/definition.json} +0 -0
- /package/{playbooks/styling → library/optional-features/styling/native-styles}/native-styles.md +0 -0
- /package/{playbooks/styling/tailwind.manifest.json → library/optional-features/styling/tailwind/definition.json} +0 -0
- /package/{playbooks/styling → library/optional-features/styling/tailwind}/tailwind-extensions.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/blade/architecture.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/blade/runtime.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/blade/security.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/blade/structure.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/blade/testing.md +0 -0
- /package/{playbooks/stack/laravel-ui.manifest.json → library/platforms/laravel-ui/definition.json} +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/architecture.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/runtime.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/security.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/structure.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/testing.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/architecture.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/runtime.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/security.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/structure.md +0 -0
- /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/testing.md +0 -0
- /package/{playbooks/platform → library/platforms/mobile}/mobile.md +0 -0
- /package/{playbooks/platform → library/platforms/web}/web.md +0 -0
- /package/{playbooks/stack → library/stacks}/expo/architecture.md +0 -0
- /package/{playbooks/stack/react-native.manifest.json → library/stacks/expo/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/expo/runtime.md +0 -0
- /package/{playbooks/stack → library/stacks}/expo/security.md +0 -0
- /package/{playbooks/stack → library/stacks}/expo/structure.md +0 -0
- /package/{playbooks/stack → library/stacks}/expo/testing.md +0 -0
- /package/{playbooks/stack → library/stacks}/laravel/architecture.md +0 -0
- /package/{playbooks/stack/laravel.manifest.json → library/stacks/laravel/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/laravel/runtime.md +0 -0
- /package/{playbooks/stack → library/stacks}/laravel/security.md +0 -0
- /package/{playbooks/stack → library/stacks}/laravel/structure.md +0 -0
- /package/{playbooks/stack → library/stacks}/laravel/testing.md +0 -0
- /package/{playbooks/stack → library/stacks}/nextjs/architecture.md +0 -0
- /package/{playbooks/stack/nextjs.manifest.json → library/stacks/nextjs/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/nextjs/runtime.md +0 -0
- /package/{playbooks/stack → library/stacks}/nextjs/security.md +0 -0
- /package/{playbooks/stack → library/stacks}/nextjs/structure.md +0 -0
- /package/{playbooks/stack → library/stacks}/nextjs/testing.md +0 -0
- /package/{playbooks/stack/no-frontend.manifest.json → library/stacks/no-frontend/definition.json} +0 -0
- /package/{playbooks/stack/none.manifest.json → library/stacks/none/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/react-vite/architecture.md +0 -0
- /package/{playbooks/stack/react-vite.manifest.json → library/stacks/react-vite/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/react-vite/runtime.md +0 -0
- /package/{playbooks/stack → library/stacks}/react-vite/security.md +0 -0
- /package/{playbooks/stack → library/stacks}/react-vite/structure.md +0 -0
- /package/{playbooks/stack → library/stacks}/react-vite/testing.md +0 -0
- /package/{playbooks/stack → library/stacks}/springboot/architecture.md +0 -0
- /package/{playbooks/stack/springboot.manifest.json → library/stacks/springboot/definition.json} +0 -0
- /package/{playbooks/stack → library/stacks}/springboot/runtime.md +0 -0
- /package/{playbooks/stack → library/stacks}/springboot/security.md +0 -0
- /package/{playbooks/stack → library/stacks}/springboot/structure.md +0 -0
- /package/{playbooks/stack → library/stacks}/springboot/testing.md +0 -0
- /package/{playbooks/universal → library/universal/accessibility}/accessibility.md +0 -0
- /package/{playbooks/universal → library/universal/coding-rules}/coding-rules.md +0 -0
- /package/{playbooks/universal → library/universal/error-handling}/error-handling.md +0 -0
- /package/{playbooks/universal → library/universal/git-conventions}/git-conventions.md +0 -0
- /package/{playbooks/universal → library/universal/observability}/observability.md +0 -0
- /package/{playbooks/universal → library/universal/security}/security.md +0 -0
- /package/{playbooks/universal → library/universal/typescript}/typescript.md +0 -0
- /package/{lib/files.js → src/engine/project-files.js} +0 -0
- /package/{lib → src/engine}/project-location.js +0 -0
- /package/{lib/application-shapes.js → src/engine/project-shapes.js} +0 -0
- /package/{lib/compatibility.js → src/engine/tested-versions.js} +0 -0
- /package/{lib → src}/stacks/context.js +0 -0
- /package/{lib/stacks/contract.js → src/stacks/rules.js} +0 -0
- /package/{ci → templates/ci}/expo.yml +0 -0
- /package/{ci → templates/ci}/laravel.yml +0 -0
- /package/{ci → templates/ci}/nextjs.yml +0 -0
- /package/{ci → templates/ci}/springboot.yml +0 -0
- /package/{ci → templates/ci}/vite.yml +0 -0
|
@@ -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
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-win-project",
|
|
3
|
-
"version": "1.
|
|
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
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"playbooks",
|
|
19
|
-
"ci",
|
|
16
|
+
"src",
|
|
17
|
+
"library",
|
|
20
18
|
"templates",
|
|
21
|
-
"
|
|
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
|
|
33
|
-
"matrix:smoke": "node
|
|
34
|
-
"matrix:full": "node
|
|
35
|
-
"verify:generated": "node
|
|
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": ">=
|
|
38
|
+
"node": ">=22.14",
|
|
39
|
+
"npm": ">=11.19"
|
|
39
40
|
},
|
|
40
41
|
"dependencies": {
|
|
41
42
|
"chalk": "5.6.2",
|
package/public/logo.svg
ADDED
|
@@ -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
|
+
}
|
package/src/cli/main.js
ADDED
|
@@ -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
|
+
}
|