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
|
@@ -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,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,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 '../
|
|
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('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>')
|
|
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
|
+
}
|
|
@@ -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}
|
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
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
|
-
}
|
package/lib/laravel-scaffold.js
DELETED