Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cta.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@
"tanstack-query",
"nitro"
]
}
}
10 changes: 10 additions & 0 deletions .cursor/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 1,
"hooks": {
"afterFileEdit": [
{
"command": "bun x ultracite fix"
}
]
}
}
27 changes: 19 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ on:
branches: ["main"]
pull_request:

# Cancel previous runs if a new commit is pushed to the same PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
validate:
name: Validate & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -15,16 +21,21 @@ jobs:
with:
bun-version: "latest"

- name: Install
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Lint (Biome)
# 1. Linting (Ultracite)
# This runs "ultracite check", respecting your biome.jsonc ignores.
# It fails fast if there are style or logic errors.
- name: Lint (Ultracite)
run: bun lint

- name: Typecheck
run: bun typecheck
# # 2. Type Checking
# # Essential because Ultracite (Biome) does not check Typescript types, only syntax.
# - name: Typecheck
# run: bun typecheck

# 3. Tests
# Only runs if the code is clean and type-safe.
- name: Test
run: bun test


run: bun test
70 changes: 70 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/sh
bun test

# Exit on any error
set -e

# Check if there are any staged files
if [ -z "$(git diff --cached --name-only)" ]; then
echo "No staged files to format"
exit 0
fi

# Store the hash of staged changes to detect modifications
STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)

# Save list of staged files (handling all file states)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
PARTIALLY_STAGED=$(git diff --name-only)

# Stash unstaged changes to preserve working directory
# --keep-index keeps staged changes in working tree
git stash push --quiet --keep-index --message "pre-commit-stash" || true
STASHED=$?

# Run formatter on the staged files
bun x ultracite fix
FORMAT_EXIT_CODE=$?

# Restore working directory state
if [ $STASHED -eq 0 ]; then
# Re-stage the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi

# Restore unstaged changes
git stash pop --quiet || true

# Restore partial staging if files were partially staged
if [ -n "$PARTIALLY_STAGED" ]; then
for file in $PARTIALLY_STAGED; do
if [ -f "$file" ] && echo "$STAGED_FILES" | grep -q "^$file$"; then
# File was partially staged - need to unstage the unstaged parts
git restore --staged "$file" 2>/dev/null || true
git add -p "$file" < /dev/null 2>/dev/null || git add "$file"
fi
done
fi
else
# No stash was created, just re-add the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi
fi

# Check if staged files actually changed
NEW_STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
if [ "$STAGED_HASH" != "$NEW_STAGED_HASH" ]; then
echo "✨ Files formatted by Ultracite"
fi

exit $FORMAT_EXIT_CODE
51 changes: 51 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,56 @@
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"emmet.showExpandedAbbreviation": "never",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[html]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[vue]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[svelte]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[css]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[yaml]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[graphql]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[markdown]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[mdx]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}
37 changes: 23 additions & 14 deletions biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["ultracite/core", "ultracite/react"],
"linter": {
"rules": {
"style": {
"useConsistentTypeDefinitions": {
"options": {
"style": "type"
},
"level": "error"
}
}
}
}
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["ultracite/core", "ultracite/react", "ultracite/biome/core"],
"linter": {
"rules": {
"style": {
"useConsistentTypeDefinitions": {
"options": {
"style": "type"
},
"level": "error"
}
}
}
},
"files": {
"includes": [
"src/**",
"!src/components/ui",
"!src/routes",
"!src/routeTree.gen.ts",
"!drizzle/*"
]
}
}
29 changes: 26 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
}
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@
"start": "bun run .output/server/index.mjs",
"test": "bun --bun vitest run",
"typecheck": "bunx tsc -p tsconfig.json --noEmit",
"lint": "bunx biome check .",
"lint": "bunx ultracite check",
"lint:fix": "bunx ultracite fix",
"db:generate": "bun --bun drizzle-kit generate",
"db:migrate": "bun --bun drizzle-kit migrate",
"db:push": "bun --bun drizzle-kit push",
"db:pull": "bun --bun drizzle-kit pull",
"db:studio": "bun --bun drizzle-kit studio",
"better-auth:generate": "bunx --bun @better-auth/cli generate --config src/features/auth/auth.ts --output src/db/schema/auth.ts",
"better-auth:secret": "bunx --bun @better-auth/cli secret"
"better-auth:secret": "bunx --bun @better-auth/cli secret",
"prepare": "husky"
},
"dependencies": {
"@fontsource-variable/geist": "^5.2.8",
Expand Down Expand Up @@ -66,7 +68,7 @@
"zod": "^4.2.1"
},
"devDependencies": {
"@biomejs/biome": "^2.3.10",
"@biomejs/biome": "2.3.10",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "^0.9.0",
"@tanstack/react-query-devtools": "^5.91.2",
Expand All @@ -83,8 +85,9 @@
"dotenv": "^17.2.3",
"drizzle-kit": "^0.31.8",
"happy-dom": "^20.0.11",
"husky": "^9.1.7",
"typescript": "^5.9.3",
"ultracite": "^6.5.0",
"ultracite": "7.0.4",
"vite": "^7.3.0",
"vite-tsconfig-paths": "^6.0.3",
"vitest": "^4.0.16"
Expand Down
9 changes: 7 additions & 2 deletions src/components/default-not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ type DefaultNotFoundProps = {
routeId: string;
};

export const DefaultNotFound: NotFoundRouteComponent = ({ isNotFound, routeId }: DefaultNotFoundProps) => (
export const DefaultNotFound: NotFoundRouteComponent = ({
isNotFound,
routeId,
}: DefaultNotFoundProps) => (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
<Card className="w-full max-w-md text-center">
<CardHeader>
<CardTitle className="text-4xl">{isNotFound ? "404" : routeId}</CardTitle>
<CardTitle className="text-4xl">
{isNotFound ? "404" : routeId}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-muted-foreground">Page not found</p>
Expand Down
7 changes: 5 additions & 2 deletions src/components/emails/verify-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,14 @@ export default function VerifyEmail({ name, verifyUrl }: VerifyEmailProps) {
{SITE_URL}
</Text>
<Text className="m-0 text-[12px] text-gray-500">
<a className="text-gray-500 no-underline" href="#">
<a className="text-gray-500 no-underline" href="/">
Unsubscribe
</a>{" "}
|
<a className="ml-[8px] text-gray-500 no-underline" href="#">
<a
className="ml-[8px] text-gray-500 no-underline"
href="/privacy-policy"
>
Privacy Policy
</a>
</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/components/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ export function Logo() {
<span className="font-semibold text-xl">tano-stack</span>
</div>
);
}
}
Loading