cleanarch-new.ps1
|
#Requires -Version 5.1 [CmdletBinding()] param( [string]$ProjectName = "", [ValidateSet("Dapper","EFCore","Hybrid","")] [string]$OrmMode = "", [ValidateSet("ReactTS","ReactJS","NextJS","")] [string]$FrontendMode = "", [string[]]$Entities = @("User", "Product"), [ValidateSet("MSSQL","Postgres","SQLite")] [string]$DbProvider = "", [string]$OutputRootPath = "", [string]$ConnectionString = "", [switch]$SkipFrontend, [switch]$SkipAiGuide, [switch]$SkipTests, [switch]$SkipCi, [switch]$DryRun, [string]$IdeConfig = "VSCode", [switch]$Observability, [switch]$NonInteractive, [switch]$I18n, [ValidateSet("GitHub","GitLab","None")] [string]$CiProvider = "GitHub", [ValidateSet("tr","en")] [string]$Language = "tr", [ValidateSet("Web","Mobile","Both","")] [string]$Platform = "", [ValidateSet("Layered","FeatureSliced")] [string]$Architecture = "Layered", [ValidateSet("Expo")] [string]$MobileTooling = "Expo", [switch]$SkipNpm, [switch]$Version, [Alias("h")] [switch]$Help ) $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $ModulesDir = Join-Path $ScriptDir "modules" # --- UTF-8 encoding (Türkçe karakter desteği) --- [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 # --- Performance Optimizations (DotNet CLI) --- $env:DOTNET_CLI_TELEMETRY_OPTOUT = "1" $env:DOTNET_NOLOGO = "1" $env:NUGET_XMLDOC_MODE = "skip" $Global:DryRun = $DryRun . "$ModulesDir\core\Utils.ps1" . "$ModulesDir\core\Invoke-TuiMenu.ps1" . "$ModulesDir\core\Get-Locale.ps1" $L = Get-Locale -Language $Language if ($Help) { Write-Host "" Write-Host " Clean Architecture Template Generator (cleanarch)" -ForegroundColor Cyan Write-Host " ------------------------------------------------" -ForegroundColor DarkCyan Write-Host " Kullanim: cleanarch-new [Parametreler]" -ForegroundColor White Write-Host "" Write-Host " PARAMETRELER:" -ForegroundColor Yellow Write-Host " -ProjectName <Ad> : Projenin adi (Bos birakilirsa sorulur)" Write-Host " -OrmMode <Mod> : Dapper, EFCore, Hybrid" Write-Host " -DbProvider <Db> : MSSQL, Postgres, SQLite" Write-Host " -FrontendMode <Fe> : ReactTS, ReactJS, NextJS" Write-Host " -Entities <Liste> : Virgulle ayrilmis entity listesi (User,Product,...)" Write-Host " -OutputRootPath <Path> : Projenin uretilecegi dizin" Write-Host " -CiProvider <Provider> : GitHub, GitLab, None (Varsayilan: GitHub)" Write-Host " -Language <Dil> : tr, en (Varsayilan: tr)" Write-Host "" Write-Host " OPSIYONEL FLAGLER:" -ForegroundColor Yellow Write-Host " -SkipFrontend : Frontend katmanini atlar" Write-Host " -SkipAiGuide : AI rehberini olusturmaz" Write-Host " -SkipTests : Test projelerini atlar" Write-Host " -SkipCi : CI/CD pipeline dosyalarini atlar" Write-Host " -Observability : Serilog ve OpenTelemetry altyapisini kurar" Write-Host " -I18n : i18next + react-i18next ile cok dil destegi ekler (en/tr)" Write-Host " -NonInteractive : TUI menusunu devre disi birakir, varsayilanlari kullanir" Write-Host " -DryRun : Dosya yazmadan islemi simule eder" Write-Host "" Write-Host " DIGER KOMUTLAR:" -ForegroundColor Yellow Write-Host " cleanarch-version : Versiyon ve degisiklik bilgilerini gosterir" Write-Host " cleanarch-update : Generator'i son surume gunceller" Write-Host " cleanarch-uninstall : Generator'i sistemden kaldirir" Write-Host "" Write-Host " Detayli dokumantasyon: docs/README.md" -ForegroundColor DarkGray Write-Host "" return } if ($Version) { $v = & "$ModulesDir\core\cleanarch-version.ps1" -OnlyVersion Write-Host "Clean Architecture Template Generator v$v" return } $TotalSw = [System.Diagnostics.Stopwatch]::StartNew() if (-not $NonInteractive) { Clear-Host } Write-Host "================================================" -ForegroundColor DarkCyan Write-Host " Clean Architecture Template Generator " -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor DarkCyan Write-Host "" # --- Proje adi --- if (-not $ProjectName) { if ($NonInteractive) { Write-Err $L.tuiNameRequired "E001"; exit 1 } $ProjectName = Read-Host $L.tuiProjectName } if (-not $ProjectName -or $ProjectName -match '[\s\\/]') { Write-Err $L.tuiInvalidName "E001" return } # --- Platform secimi --- if (-not $Platform) { if ($NonInteractive) { $Platform = "Web" } else { $options = @( "Web App Only (React Vite/Next.js)", "Mobile App Only (React Native / Expo)", "Both Web & Mobile Monorepo" ) $choice = Invoke-TuiMenu -Title $L.tuiSelectPlatform -Options $options $Platform = switch ($choice) { 0 { "Web" } 1 { "Mobile" } 2 { "Both" } } } } # --- Architecture secimi --- if (-not $Architecture) { if ($NonInteractive) { $Architecture = "Layered" } else { $options = @( "Layered Architecture (Traditional Clean Arch)", "Feature-Sliced Design (FSD Experimental)" ) $choice = Invoke-TuiMenu -Title $L.tuiSelectArch -Options $options $Architecture = switch ($choice) { 0 { "Layered" } 1 { "FeatureSliced" } } } } # --- ORM secimi --- if (-not $OrmMode) { if ($NonInteractive) { $OrmMode = "EFCore" } else { $options = @( "Dapper Only (micro-ORM, raw SQL)", "EF Core Only (code-first, migrations)", "Hybrid (Yaz: Dapper | Oku: EF Core)" ) $choice = Invoke-TuiMenu -Title $L.tuiSelectOrm -Options $options $OrmMode = switch ($choice) { 0 { "Dapper" } 1 { "EFCore" } 2 { "Hybrid" } } } } # --- Database secimi --- if (-not $DbProvider) { if ($NonInteractive) { $DbProvider = "SQLite" } else { $options = @("MS SQL Server", "PostgreSQL", "SQLite") $choice = Invoke-TuiMenu -Title $L.tuiSelectDb -Options $options $DbProvider = switch ($choice) { 0 { "MSSQL" } 1 { "Postgres" } 2 { "SQLite" } } } } # --- Connection String secimi --- if (-not $ConnectionString) { if ($NonInteractive) { $ConnectionString = "" } else { $options = @( "Varsayilan degerleri kullan (Lokal / Docker uyumlu)", "Kendi baglanti dizemi girmek istiyorum" ) $choice = Invoke-TuiMenu -Title "Baglanti Dizesi (Connection String) Secin:" -Options $options if ($choice -eq 1) { Write-Host "" $ConnectionString = Read-Host "Lutfen veritabanı baglanti dizesini girin" Write-Host "" } } } # --- Frontend framework --- if (-not $FrontendMode -and -not $SkipFrontend -and $Platform -ne "Mobile") { if ($NonInteractive) { $FrontendMode = "ReactTS" } else { $options = @( "React + Vite (TypeScript)", "React + Vite (JavaScript)", "Next.js (App Router + TypeScript)" ) $choice = Invoke-TuiMenu -Title $L.tuiSelectFrontend -Options $options $FrontendMode = switch ($choice) { 0 { "ReactTS" } 1 { "ReactJS" } 2 { "NextJS" } } } } # --- CI Provider --- if ($CiProvider -eq "GitHub" -and -not $SkipCi -and -not $NonInteractive) { # Eğer parametre ile verilmediyse (varsayılan GitHub olduğu için kontrolü parametre verilip verilmediğini anlamak için bazen zor olabilir, # ancak burada kullanıcıya sormak iyi bir UX'tir) $options = @("GitHub Actions", "GitLab CI", "None (Skip CI)") $choice = Invoke-TuiMenu -Title $L.tuiSelectCiCd -Options $options $CiProvider = switch ($choice) { 0 { "GitHub" } 1 { "GitLab" } 2 { "None" } } if ($CiProvider -eq "None") { $SkipCi = $true } } # --- Entities string split (virgülle geçilen "Product,Order" -> @("Product","Order")) --- if ($Entities.Count -eq 1 -and $Entities[0] -match ',') { $Entities = $Entities[0] -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } } # --- 'User' entity'si Auth icin zorunludur --- if ($Entities -notcontains "User") { $Entities += "User" } # --- Cikti koku --- if (-not $OutputRootPath) { $currentDir = (Get-Location).Path if ($NonInteractive) { $OutputRootPath = $currentDir } else { Write-Host "" Write-Host ($L.tuiProjectDir -f $currentDir) -ForegroundColor DarkGray $inputRoot = Read-Host $L.tuiDirPrompt $OutputRootPath = if ($inputRoot.Trim()) { $inputRoot.Trim() } else { $currentDir } } } if (-not $Global:DryRun -and -not (Test-Path $OutputRootPath)) { New-Item -ItemType Directory -Path $OutputRootPath -Force | Out-Null } $OutputRootPath = [System.IO.Path]::GetFullPath($OutputRootPath) $OutputPath = Join-Path $OutputRootPath $ProjectName if (-not $Global:DryRun -and (Test-Path $OutputPath)) { Write-Err ($L.tuiDirExists -f $OutputPath) "E002" return } # Params Splatting $Params = @{ ProjectName = $ProjectName OrmMode = $OrmMode DbProvider = $DbProvider OutputPath = $OutputPath FrontendMode = $FrontendMode SkipFrontend = $SkipFrontend SkipAiGuide = $SkipAiGuide SkipTests = $SkipTests SkipCi = $SkipCi CiProvider = $CiProvider DryRun = $DryRun Entities = $Entities IdeConfig = $IdeConfig Observability = $Observability I18n = $I18n Language = $Language Platform = $Platform Architecture = $Architecture MobileTooling = $MobileTooling SkipNpm = $SkipNpm ConnectionString = $ConnectionString } # --- Adimlar --- & "$ModulesDir\core\Test-Prerequisites.ps1" -FrontendMode $FrontendMode if ($LASTEXITCODE -ne 0) { exit 1 } try { Invoke-Step $L.logCreatingSolution { & "$ModulesDir\solution\Initialize-Solution.ps1" @Params } Invoke-Step $L.logInstallingNuget { & "$ModulesDir\solution\Install-Packages.ps1" @Params } Invoke-Step $L.logDomain { & "$ModulesDir\backend\Write-DomainLayer.ps1" @Params } Invoke-Step $L.logApplication { & "$ModulesDir\backend\Write-ApplicationLayer.ps1" @Params } Invoke-Step $L.logInfrastructure { & "$ModulesDir\backend\Write-InfrastructureLayer.ps1" @Params } Invoke-Step $L.logPersistence { & "$ModulesDir\backend\Write-Persistence-Shared.ps1" @Params & "$ModulesDir\backend\Write-Persistence-Dapper.ps1" @Params & "$ModulesDir\backend\Write-Persistence-EFCore.ps1" @Params & "$ModulesDir\backend\Write-Persistence-Hybrid.ps1" @Params } Invoke-Step $L.logWebApi { & "$ModulesDir\backend\Write-WebApiLayer.ps1" @Params } if (-not $SkipTests) { Invoke-Step $L.logTests { & "$ModulesDir\testing\Write-TestLayer.ps1" @Params } } if (-not $SkipFrontend) { Invoke-Step $L.logFrontendSetup { & "$ModulesDir\solution\Install-Frontend.ps1" @Params } Invoke-Step $L.logFrontendFiles { & "$ModulesDir\frontend\Write-Frontend.ps1" @Params } } Invoke-Step $L.logDocker { & "$ModulesDir\devops\Write-Docker.ps1" @Params } if ($IdeConfig -ne "None") { Invoke-Step "$IdeConfig configuration" { & "$ModulesDir\devops\Write-IdeConfig.ps1" @Params } } if (-not $SkipCi) { Invoke-Step $L.logCiCd { & "$ModulesDir\devops\Write-CiPipeline.ps1" @Params } } Invoke-Step $L.logMigrations { & "$ModulesDir\devops\Write-MigrationScript.ps1" @Params } Invoke-Step $L.logReadme { & "$ModulesDir\solution\Write-Readme.ps1" @Params } if (-not $SkipAiGuide) { Invoke-Step $L.logAiGuide { & "$ModulesDir\ai-guide\Write-AiGuide-Core.ps1" @Params & "$ModulesDir\ai-guide\Write-AiGuide-Endpoints.ps1" @Params & "$ModulesDir\ai-guide\Write-AiGuide-Frontend.ps1" @Params } } # Summary Write-Host "" Write-Host "================================================" -ForegroundColor Green Write-Host (" " + ($L.tuiReady -f $ProjectName)) -ForegroundColor Green Write-Host (" " + ($L.tuiTotalTime -f $TotalSw.Elapsed.TotalSeconds)) -ForegroundColor Green Write-Host "================================================" -ForegroundColor Green Write-Host "" Write-Host "$($L.tuiLocation) : $OutputPath" Write-Host "ORM : $OrmMode" Write-Host "Database : $DbProvider" Write-Host "Frontend : $(if($SkipFrontend){'None'}else{$FrontendMode})" Write-Host "IDE : $IdeConfig" Write-Host "" } catch { Write-Err ($L.tuiErrorOccurred -f $_) "E003" if (-not $Global:DryRun) { Write-Warn $L.tuiCleaningUp Remove-Item $OutputPath -Recurse -Force -ErrorAction SilentlyContinue } return } |