# Save this as scaffold.ps1, then run: # pwsh -NoProfile -ExecutionPolicy Bypass -File ./scaffold.ps1 $ErrorActionPreference = 'Stop' function Write-Utf8([string]$Path, [string]$Content) { $parent = Split-Path -Parent $Path if ($parent -and -not (Test-Path $parent)) { New-Item -ItemType Directory -Force -Path $parent | Out-Null } $Content | Set-Content -Encoding UTF8 -Path $Path } # Create folders $dirs = @( 'app','app/(routes)','app/(routes)/about','app/(routes)/services','app/(routes)/showreel','app/(routes)/testimonials','app/(routes)/contact','app/(routes)/legal', 'components','styles','locales','locales/en','locales/de','lib','tests','tests/unit','tests/e2e','public' ) foreach($d in $dirs){ if(!(Test-Path $d)){ New-Item -ItemType Directory -Path $d | Out-Null } } # .gitignore $gitignore = @' # Node node_modules .next out package-lock.json yarn.lock pnpm-lock.yaml # Testing test-results/ playwright-report/ playwright/.cache/ coverage/ # Env .env.local .env .DS_Store # Logs npm-debug.log* yarn-debug.log* yarn-error.log* '@ Write-Utf8 '.gitignore' $gitignore # package.json $packageJson = @' { "name": "michaelpeskov-site", "private": true, "version": "0.1.0", "type": "module", "scripts": { "dev": "next dev", "build": "next build", "start": "next start -p 3000", "lint": "next lint", "test": "jest --runInBand", "test:e2e": "playwright test" }, "dependencies": { "next": "14.2.5", "react": "18.3.1", "react-dom": "18.3.1" }, "devDependencies": { "@playwright/test": "^1.44.1", "@types/jest": "^29.5.12", "@types/node": "^20.11.30", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "eslint": "^8.57.0", "eslint-config-next": "14.2.5", "jest": "^29.7.0", "ts-jest": "^29.1.2", "typescript": "^5.4.5" } } '@ Write-Utf8 'package.json' $packageJson # tsconfig.json $tsconfig = @' { "compilerOptions": { "target": "ES2022", "lib": ["ES2022", "DOM", "DOM.Iterable"], "jsx": "preserve", "module": "ESNext", "moduleResolution": "Bundler", "resolveJsonModule": true, "noEmit": true, "allowJs": false, "incremental": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "baseUrl": ".", "paths": { "@/components/*": ["components/*"], "@/styles/*": ["styles/*"], "@/lib/*": ["lib/*"], "@/locales/*": ["locales/*"] }, "types": ["jest", "node"] } } '@ Write-Utf8 'tsconfig.json' $tsconfig # next.config.ts $nextConfig = @' import type { NextConfig } from 'next' const nextConfig: NextConfig = { reactStrictMode: true, experimental: { typedRoutes: true }, images: { formats: ['image/avif', 'image/webp'], remotePatterns: [] }, headers: async () => [ { source: '/(.*)', headers: [ { key: 'Permissions-Policy', value: 'geolocation=(), microphone=(), camera=()' } ] } ] } export default nextConfig '@ Write-Utf8 'next.config.ts' $nextConfig # jest.config.ts $jestConfig = @' import type { Config } from 'jest' const config: Config = { testEnvironment: 'node', transform: { '^.+\\.tsx?$': [ 'ts-jest', { tsconfig: 'tsconfig.json', isolatedModules: true } ] }, testMatch: ['**/tests/unit/**/*.test.ts'], moduleNameMapper: { '^@/components/(.*)$': '/components/$1', '^@/lib/(.*)$': '/lib/$1', '^@/locales/(.*)$': '/locales/$1', '^@/styles/(.*)$': '/styles/$1' } } export default config '@ Write-Utf8 'jest.config.ts' $jestConfig # playwright.config.ts $pwConfig = @' import { defineConfig, devices } from '@playwright/test' export default defineConfig({ timeout: 30000, testDir: 'tests/e2e', webServer: { command: 'npm run dev', port: 3000, reuseExistingServer: !process.env.CI }, use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', viewport: { width: 1280, height: 800 } }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } } ] }) '@ Write-Utf8 'playwright.config.ts' $pwConfig # app/layout.tsx $appLayout = @' import './globals.css' import '@/styles/tokens.css' import type { Metadata, Viewport } from 'next' import { cookies, headers } from 'next/headers' import { getDictionary, Locale, getInitialLocale } from '@/lib/i18n' import LanguageToggle from '@/components/LanguageToggle' import Link from 'next/link' export const metadata: Metadata = { title: 'Michael Peskov Magician & Pickpocket | Zauberer Solingen & NRW', description: 'Seen on SAT.1, WDR, ZDF & Amazon Prime Video. 5 rated magician for corporate events, weddings & more. Based in Solingen, travels >1000 km.', metadataBase: new URL('http://localhost:3000'), alternates: { languages: { en: '/', de: '/?lang=de' } }, openGraph: { title: 'Michael Peskov Modern Magician & Pickpocket', description: '5/5 stars, 12 reviews, 20+ bookings, 100% recommendation; since 12/2022 on Eventpeppers.', url: '/', siteName: 'Michael Peskov', images: [{ url: '/hero-poster.webp', width: 1200, height: 630, alt: 'Michael performing' }] } } export const viewport: Viewport = { themeColor: '#0B0C10', colorScheme: 'dark' } export default async function RootLayout({ children }: { children: React.ReactNode }) { const cookieStore = cookies() const hdrs = headers() const initialLocale = getInitialLocale(cookieStore, hdrs) as Locale const dict = await getDictionary(initialLocale) return ( Skip to content
Michael Peskov
{dict.common.cta.bookNow}
{children}
{dict.common.cta.bookNow} / {dict.common.cta.bookNowAlt}
Michael Peskov
Modern Magician & Pickpocket Solingen
SAT.1 WDR ZDF Amazon Prime Video
Services
Showreel
Testimonials
About
Contact
Impressum / Datenschutz
{new Date().getFullYear()} Michael Peskov
) } '@ Write-Utf8 'app/layout.tsx' $appLayout # app/page.tsx $homePage = @' import { cookies, headers } from 'next/headers' import { getDictionary, getInitialLocale } from '@/lib/i18n' import Hero from '@/components/Hero' export default async function HomePage() { const cookieStore = cookies() const hdrs = headers() const locale = getInitialLocale(cookieStore, hdrs) const dict = await getDictionary(locale) return (<>) } '@ Write-Utf8 'app/page.tsx' $homePage # routes pages $about = @' import { cookies, headers } from 'next/headers' import { getDictionary, getInitialLocale } from '@/lib/i18n' export default async function AboutPage() { const cookieStore = cookies() const hdrs = headers() const locale = getInitialLocale(cookieStore, hdrs) const dict = await getDictionary(locale) return (

{dict.about.title}

{dict.about.blurb}

) } '@ Write-Utf8 'app/(routes)/about/page.tsx' $about $services = @' import { cookies, headers } from 'next/headers' import { getDictionary, getInitialLocale } from '@/lib/i18n' export default async function ServicesPage() { const cookieStore = cookies() const hdrs = headers() const locale = getInitialLocale(cookieStore, hdrs) const dict = await getDictionary(locale) return (

{dict.services.title}

  • {dict.services.closeup.title} {dict.services.closeup.desc}
  • {dict.services.stage.title} {dict.services.stage.desc}
  • {dict.services.pickpocket.title} {dict.services.pickpocket.desc}
  • {dict.services.fork.title} {dict.services.fork.desc}
) } '@ Write-Utf8 'app/(routes)/services/page.tsx' $services $showreel = @' import { cookies, headers } from 'next/headers' import { getDictionary, getInitialLocale } from '@/lib/i18n' export default async function ShowreelPage() { const cookieStore = cookies() const hdrs = headers() const locale = getInitialLocale(cookieStore, hdrs) const dict = await getDictionary(locale) return (

{dict.showreel.title}

{dict.showreel.note}