Commit:
e9c5ce1Parent:
c8f0d9eInitialize SvelteKit project with Deno, TypeScript, ESLint, and Playwright
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.claude/settings.json
+5
-0
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 0000000..ffd67f5
@@ -0,0 +1,5 @@
{
"env": {
"DENO_DIR": "/home/developer/.cache/deno"
}
}
.gitignore
+25
-0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..41f1bdc
@@ -0,0 +1,25 @@
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Playwright
test-results
.npmrc
+1
-0
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..b6f27f1
@@ -0,0 +1 @@
engine-strict=true
.vscode/extensions.json
+6
-0
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..13b64ef
@@ -0,0 +1,6 @@
{
"recommendations": [
"svelte.svelte-vscode",
"dbaeumer.vscode-eslint"
]
}
CLAUDE.md
+52
-0
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..ed8e922
@@ -0,0 +1,52 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
All tasks run via Deno. Use `deno task` instead of `npm run`.
```bash
deno task dev # start dev server
deno task build # production build
deno task preview # preview production build
deno task check # svelte-kit sync + svelte-check (type checking)
deno task lint # ESLint
deno task test # run all Playwright e2e tests
```
Run a single e2e test file:
```bash
DENO_DIR=/home/developer/.cache/deno deno run -A npm:playwright test src/routes/path/to/page.svelte.e2e.ts
```
Format source files (Svelte component formatting requires the `fmt-component` unstable flag, already set in `deno.json`):
```bash
deno fmt
```
## Architecture
This is a **SvelteKit** app using **Svelte 5** with **Deno** as the runtime and package manager.
### Key conventions
- **Svelte 5 runes mode is enforced globally** via `svelte.config.js`. All components must use the runes API (`$props()`, `$state()`, `$derived()`, `$effect()`, etc.). The `runes: true` compiler option is applied to every file outside `node_modules`.
- **`$lib`** maps to `src/lib/`. Put shared components, utilities, and assets there.
- **File-based routing** under `src/routes/`. Each route segment is a directory; `+page.svelte` is the page, `+layout.svelte` wraps child routes.
- **E2e tests are collocated with routes** using the `.e2e.{ts,js}` extension (e.g., `src/routes/demo/playwright/page.svelte.e2e.ts`). Playwright's `testMatch` is set to this pattern.
### Config files
- `deno.json` — Deno tasks, formatter config (`fmt-component` unstable flag for Svelte files), and the `DENO_DIR` cache.
- `package.json` — npm-compatible dependency declarations consumed by Deno's npm compatibility layer.
- `svelte.config.js` — SvelteKit adapter (`adapter-auto`) and runes enforcement.
- `playwright.config.ts` — e2e tests run against a **production build** (not dev server) on port 4173.
- `tsconfig.json` — extends the generated `.svelte-kit/tsconfig.json`; strict mode enabled.
### Environment
The Deno npm cache is not at the default location. When running `deno` commands directly (not via `deno task`), set:
```bash
DENO_DIR=/home/developer/.cache/deno
```
README.md
+41
-2
diff --git a/README.md b/README.md
index d1a6c6b..f3a6f53 100644
@@ -1,3 +1,42 @@
# BirdGO
# sv
Catch (the songs of) them all!
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```sh
# create a new project
npx sv create my-app
```
To recreate this project with the same configuration:
```sh
# recreate this project
deno run npm:sv@0.15.3 create --template minimal --types ts --add eslint playwright --no-download-check --install deno /home/developer/BirdGO
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```sh
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```sh
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
deno.json
+17
-0
diff --git a/deno.json b/deno.json
new file mode 100644
index 0000000..1cd361f
@@ -0,0 +1,17 @@
{
"unstable": ["fmt-component"],
"fmt": {
"useTabs": false,
"singleQuote": false,
"include": ["src/"]
},
"tasks": {
"dev": "deno run -A npm:vite dev",
"build": "deno run -A npm:vite build",
"preview": "deno run -A npm:vite preview",
"check": "deno run -A npm:svelte-kit sync && deno run -A npm:svelte-check --tsconfig ./tsconfig.json",
"lint": "deno run -A npm:eslint .",
"test:e2e": "deno run -A npm:playwright install && deno run -A npm:playwright test",
"test": "deno task test:e2e"
}
}
deno.lock
+1190
-0
Large diff (1190 lines changed) - not displayed
eslint.config.js
+41
-0
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..97cb8a3
@@ -0,0 +1,41 @@
import path from 'node:path';
import { includeIgnoreFile } from '@eslint/compat';
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import ts from 'typescript-eslint';
import svelteConfig from './svelte.config.js';
const gitignorePath = path.resolve(import.meta.dirname, '.gitignore');
export default defineConfig(
includeIgnoreFile(gitignorePath),
js.configs.recommended,
ts.configs.recommended,
svelte.configs.recommended,
{
languageOptions: { globals: { ...globals.browser, ...globals.node } },
rules: {
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
"no-undef": 'off'
}
},
{
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
languageOptions: {
parserOptions: {
projectService: true,
extraFileExtensions: ['.svelte'],
parser: ts.parser,
svelteConfig
}
}
},
{
// Override or add rule settings here, such as:
// 'svelte/button-has-type': 'error'
rules: {}
}
);
package.json
+34
-0
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fc26a4f
@@ -0,0 +1,34 @@
{
"name": "birdgo",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "eslint .",
"test:e2e": "playwright install && playwright test",
"test": "npm run test:e2e"
},
"devDependencies": {
"@eslint/compat": "^2.0.4",
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.59.1",
"@sveltejs/adapter-auto": "^7.0.1",
"@sveltejs/kit": "^2.57.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@types/node": "^24",
"eslint": "^10.2.0",
"eslint-plugin-svelte": "^3.17.0",
"globals": "^17.4.0",
"svelte": "^5.55.2",
"svelte-check": "^4.4.6",
"typescript": "^6.0.2",
"typescript-eslint": "^8.58.1",
"vite": "^8.0.7"
}
}
playwright.config.ts
+6
-0
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..9eea31b
@@ -0,0 +1,6 @@
import { defineConfig } from '@playwright/test';
export default defineConfig({
webServer: { command: 'npm run build && npm run preview', port: 4173 },
testMatch: '**/*.e2e.{ts,js}'
});
src/app.d.ts
+13
-0
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000..520c421
@@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};
src/app.html
+12
-0
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..aa3be3b
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="text-scale" content="scale" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
src/lib/assets/favicon.svg
+16
-0
diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg
new file mode 100644
index 0000000..77c50de
@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="107"
height="128"
viewBox="0 0 107 128"
>
<title>svelte-logo</title>
<path
d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116"
style="fill: #ff3e00"
/>
<path
d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328"
style="fill: #fff"
/>
</svg>
src/lib/index.ts
+1
-0
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000..856f2b6
@@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.
src/routes/+layout.svelte
+11
-0
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..ccc155c
@@ -0,0 +1,11 @@
<script lang="ts">
import favicon from "$lib/assets/favicon.svg";
let { children } = $props();
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
{@render children()}
src/routes/+page.svelte
+5
-0
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..3d70440
@@ -0,0 +1,5 @@
<h1>Welcome to SvelteKit</h1>
<p>
Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read
the documentation
</p>
src/routes/demo/+page.svelte
+5
-0
diff --git a/src/routes/demo/+page.svelte b/src/routes/demo/+page.svelte
new file mode 100644
index 0000000..af49d66
@@ -0,0 +1,5 @@
<script lang="ts">
import { resolve } from "$app/paths";
</script>
<a href={resolve("/demo/playwright")}>playwright</a>
src/routes/demo/playwright/+page.svelte
+1
-0
diff --git a/src/routes/demo/playwright/+page.svelte b/src/routes/demo/playwright/+page.svelte
new file mode 100644
index 0000000..5f0f2c9
@@ -0,0 +1 @@
<h1>Playwright e2e test demo</h1>
src/routes/demo/playwright/page.svelte.e2e.ts
+6
-0
diff --git a/src/routes/demo/playwright/page.svelte.e2e.ts b/src/routes/demo/playwright/page.svelte.e2e.ts
new file mode 100644
index 0000000..d450498
@@ -0,0 +1,6 @@
import { expect, test } from "@playwright/test";
test("has expected h1", async ({ page }) => {
await page.goto("/demo/playwright");
await expect(page.locator("h1")).toBeVisible();
});
static/robots.txt
+3
-0
diff --git a/static/robots.txt b/static/robots.txt
new file mode 100644
index 0000000..b6dd667
@@ -0,0 +1,3 @@
# allow crawling everything by default
User-agent: *
Disallow:
svelte.config.js
+17
-0
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..0c3412e
@@ -0,0 +1,17 @@
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
compilerOptions: {
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true)
},
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;
tsconfig.json
+20
-0
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..2c2ed3c
@@ -0,0 +1,20 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// To make changes to top-level options such as include and exclude, we recommend extending
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
}
vite.config.ts
+6
-0
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..bbf8c7d
@@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});