diff --git a/.husky/pre-commit b/.husky/pre-commit index ee2ec94c8..30c1cf220 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,2 +1,5 @@ pnpm exec lint-staged pnpm run type-check + +# Run svelte-check on staged web apps (catches a11y, imports, Svelte 5 issues) +./scripts/svelte-check-staged.sh diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 000000000..58a8531d3 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,3 @@ +# Optional: Run production build check before push +# Uncomment to enable (slower but catches all CI issues) +# ./scripts/build-check-staged.sh diff --git a/lint-staged.config.js b/lint-staged.config.js index 308ffddc9..94417196d 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -3,5 +3,10 @@ export default { 'eslint --fix --ignore-pattern "apps-archived/**"', 'prettier --config .prettierrc.json --write', ], - '*.{json,md,svelte,astro}': ['prettier --config .prettierrc.json --write'], + '*.{json,md,astro}': ['prettier --config .prettierrc.json --write'], + // Svelte files: format + check for a11y and Svelte 5 issues + '*.svelte': [ + 'prettier --config .prettierrc.json --write', + // svelte-check is run at project level via pre-commit hook + ], }; diff --git a/package.json b/package.json index 1676ac859..dff273c23 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ "clean": "turbo run clean", "format": "prettier --config .prettierrc.json --write \"**/*.{ts,tsx,js,jsx,json,md,svelte,astro}\"", "format:check": "prettier --config .prettierrc.json --check \"**/*.{ts,tsx,js,jsx,json,md,svelte,astro}\"", + "svelte-check": "./scripts/svelte-check-staged.sh", + "build:check": "./scripts/build-check-staged.sh", + "build:check:all": "./scripts/build-check-staged.sh --all", "setup:env": "node scripts/generate-env.mjs", "setup:db": "./scripts/setup-databases.sh", "setup:db:chat": "./scripts/setup-databases.sh chat", diff --git a/scripts/build-check-staged.sh b/scripts/build-check-staged.sh new file mode 100755 index 000000000..e391cadb5 --- /dev/null +++ b/scripts/build-check-staged.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# Run production builds on web apps that have changes since main/dev +# This catches issues that only appear in production builds +# +# Usage: +# ./scripts/build-check-staged.sh # Check changes vs origin/dev +# ./scripts/build-check-staged.sh main # Check changes vs origin/main +# ./scripts/build-check-staged.sh --all # Check all web apps + +set -e + +BASE_BRANCH="${1:-origin/dev}" + +# Handle --all flag +if [ "$1" = "--all" ]; then + echo "🔨 Building ALL web apps..." + APPS_TO_BUILD=( + "apps/todo/apps/web" + "apps/chat/apps/web" + "apps/calendar/apps/web" + "apps/clock/apps/web" + "apps/manacore/apps/web" + "apps/contacts/apps/web" + "apps/zitare/apps/web" + "apps/picture/apps/web" + "apps/manadeck/apps/web" + ) +else + echo "🔍 Finding changed files since $BASE_BRANCH..." + + # Get list of changed files + CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null || git diff --name-only HEAD~10...HEAD) + + if [ -z "$CHANGED_FILES" ]; then + echo "No changes detected" + exit 0 + fi + + # Find unique web app directories that have changes + declare -A WEB_APPS + + for file in $CHANGED_FILES; do + # Direct changes in web app + if [[ $file =~ ^(apps/[^/]+/apps/web)/ ]]; then + WEB_APPS["${BASH_REMATCH[1]}"]=1 + elif [[ $file =~ ^(games/[^/]+/apps/web)/ ]]; then + WEB_APPS["${BASH_REMATCH[1]}"]=1 + # Changes in shared packages affect all web apps using them + elif [[ $file =~ ^packages/shared- ]]; then + echo "⚠️ Shared package changed: $file" + echo " All web apps may be affected" + # Add major web apps + WEB_APPS["apps/todo/apps/web"]=1 + WEB_APPS["apps/chat/apps/web"]=1 + WEB_APPS["apps/calendar/apps/web"]=1 + WEB_APPS["apps/manacore/apps/web"]=1 + fi + done + + APPS_TO_BUILD=("${!WEB_APPS[@]}") +fi + +if [ ${#APPS_TO_BUILD[@]} -eq 0 ]; then + echo "No web app changes detected" + exit 0 +fi + +echo "" +echo "🔨 Building affected web apps..." +echo " Apps: ${APPS_TO_BUILD[*]}" +echo "" + +# First build shared packages (needed for web apps) +echo "━━━ Building shared packages ━━━" +pnpm run build:packages || { + echo "❌ Failed to build shared packages" + exit 1 +} + +FAILED=0 + +for app in "${APPS_TO_BUILD[@]}"; do + if [ -f "$app/package.json" ]; then + echo "" + echo "━━━ Building $app ━━━" + + PKG_NAME=$(node -p "require('./$app/package.json').name" 2>/dev/null || echo "") + + if [ -n "$PKG_NAME" ]; then + if ! pnpm --filter "$PKG_NAME" build 2>&1; then + echo "❌ Build failed for $app" + FAILED=1 + else + echo "✅ Build passed for $app" + fi + fi + fi +done + +if [ $FAILED -eq 1 ]; then + echo "" + echo "❌ Build check failed! Fix the issues above before pushing." + exit 1 +fi + +echo "" +echo "✅ All builds passed!" diff --git a/scripts/svelte-check-staged.sh b/scripts/svelte-check-staged.sh new file mode 100755 index 000000000..9ed1caf78 --- /dev/null +++ b/scripts/svelte-check-staged.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Run svelte-check on web apps that have staged .svelte files +# This catches a11y warnings, Svelte 5 issues, and import errors before CI + +set -e + +# Get list of staged svelte files +STAGED_SVELTE=$(git diff --cached --name-only --diff-filter=ACM | grep '\.svelte$' || true) + +if [ -z "$STAGED_SVELTE" ]; then + echo "No staged .svelte files, skipping svelte-check" + exit 0 +fi + +# Find unique web app directories that have changes +declare -A WEB_APPS + +for file in $STAGED_SVELTE; do + # Extract the web app path (e.g., apps/todo/apps/web) + if [[ $file =~ ^(apps/[^/]+/apps/web)/ ]]; then + WEB_APPS["${BASH_REMATCH[1]}"]=1 + elif [[ $file =~ ^(games/[^/]+/apps/web)/ ]]; then + WEB_APPS["${BASH_REMATCH[1]}"]=1 + elif [[ $file =~ ^(packages/[^/]+)/ ]]; then + # For shared packages, check all web apps that might use them + # This is a simplified approach - just warn + echo "⚠️ Changes in shared package: $file" + echo " Consider running: pnpm run build:check to verify all web apps" + fi +done + +if [ ${#WEB_APPS[@]} -eq 0 ]; then + echo "No web app changes detected" + exit 0 +fi + +echo "🔍 Running svelte-check on affected web apps..." +FAILED=0 + +for app in "${!WEB_APPS[@]}"; do + if [ -f "$app/package.json" ]; then + echo "" + echo "━━━ Checking $app ━━━" + + # Get the package name for pnpm filter + PKG_NAME=$(node -p "require('./$app/package.json').name" 2>/dev/null || echo "") + + if [ -n "$PKG_NAME" ]; then + # Run svelte-check with threshold to fail on warnings + if ! pnpm --filter "$PKG_NAME" exec svelte-check --tsconfig ./tsconfig.json --threshold warning 2>&1; then + echo "❌ svelte-check failed for $app" + FAILED=1 + else + echo "✅ svelte-check passed for $app" + fi + fi + fi +done + +if [ $FAILED -eq 1 ]; then + echo "" + echo "❌ svelte-check failed! Fix the issues above before committing." + echo "" + echo "Common fixes:" + echo " - Add role and tabindex to interactive divs" + echo " - Add onkeydown handler alongside onclick" + echo " - Use \$state() for reactive variables in Svelte 5" + echo " - Check that all imports resolve correctly" + exit 1 +fi + +echo "" +echo "✅ All svelte-checks passed!"