feat: remove README and cursor rules, update dependencies, and implement authentication middleware
This commit is contained in:
22
.cursorrules
22
.cursorrules
@@ -1,22 +0,0 @@
|
||||
We use Sentry for watching for errors in our deployed application, as well as for instrumentation of our application.
|
||||
|
||||
## Error collection
|
||||
|
||||
Error collection is automatic and configured in `src/router.tsx`.
|
||||
|
||||
## Instrumentation
|
||||
|
||||
We want our server functions instrumented. So if you see a function name like `createServerFn`, you can instrument it with Sentry. You'll need to import `Sentry`:
|
||||
|
||||
```tsx
|
||||
import * as Sentry from '@sentry/tanstackstart-react'
|
||||
```
|
||||
|
||||
And then wrap the implementation of the server function with `Sentry.startSpan`, like so:
|
||||
|
||||
```tsx
|
||||
Sentry.startSpan({ name: 'Requesting all the pokemon' }, async () => {
|
||||
// Some lengthy operation here
|
||||
await fetch('https://api.pokemon.com/data/')
|
||||
})
|
||||
```
|
||||
51
CLAUDE.md
Normal file
51
CLAUDE.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# CLAUDE.md
|
||||
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
||||
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||||
1. Think Before Coding
|
||||
Don't assume. Don't hide confusion. Surface tradeoffs.
|
||||
Before implementing:
|
||||
|
||||
* State your assumptions explicitly. If uncertain, ask.
|
||||
* If multiple interpretations exist, present them - don't pick silently.
|
||||
* If a simpler approach exists, say so. Push back when warranted.
|
||||
* If something is unclear, stop. Name what's confusing. Ask.
|
||||
2. Simplicity First
|
||||
Minimum code that solves the problem. Nothing speculative.
|
||||
|
||||
* No features beyond what was asked.
|
||||
* No abstractions for single-use code.
|
||||
* No "flexibility" or "configurability" that wasn't requested.
|
||||
* No error handling for impossible scenarios.
|
||||
* If you write 200 lines and it could be 50, rewrite it.
|
||||
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||||
3. Surgical Changes
|
||||
Touch only what you must. Clean up only your own mess.
|
||||
When editing existing code:
|
||||
|
||||
* Don't "improve" adjacent code, comments, or formatting.
|
||||
* Don't refactor things that aren't broken.
|
||||
* Match existing style, even if you'd do it differently.
|
||||
* If you notice unrelated dead code, mention it - don't delete it.
|
||||
When your changes create orphans:
|
||||
|
||||
* Remove imports/variables/functions that YOUR changes made unused.
|
||||
* Don't remove pre-existing dead code unless asked.
|
||||
The test: Every changed line should trace directly to the user's request.
|
||||
4. Goal-Driven Execution
|
||||
Define success criteria. Loop until verified.
|
||||
Transform tasks into verifiable goals:
|
||||
|
||||
* "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||||
* "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||||
* "Refactor X" → "Ensure tests pass before and after"
|
||||
For multi-step tasks, state a brief plan:
|
||||
|
||||
```
|
||||
1. [Step] → verify: [check]
|
||||
2. [Step] → verify: [check]
|
||||
3. [Step] → verify: [check]
|
||||
|
||||
```
|
||||
|
||||
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||||
These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
||||
222
README.md
222
README.md
@@ -1,222 +0,0 @@
|
||||
Welcome to your new TanStack Start app!
|
||||
|
||||
# Getting Started
|
||||
|
||||
To run this application:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
# Building For Production
|
||||
|
||||
To build this application for production:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
|
||||
|
||||
### Removing Tailwind CSS
|
||||
|
||||
If you prefer not to use Tailwind CSS:
|
||||
|
||||
1. Remove the demo pages in `src/routes/demo/`
|
||||
2. Replace the Tailwind import in `src/styles.css` with your own styles
|
||||
3. Remove `tailwindcss()` from the plugins array in `vite.config.ts`
|
||||
4. Uninstall the packages: `npm install @tailwindcss/vite tailwindcss -D`
|
||||
|
||||
## Linting & Formatting
|
||||
|
||||
This project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:
|
||||
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
npm run format
|
||||
npm run check
|
||||
```
|
||||
|
||||
|
||||
## Shadcn
|
||||
|
||||
Add components using the latest version of [Shadcn](https://ui.shadcn.com/).
|
||||
|
||||
```bash
|
||||
pnpm dlx shadcn@latest add button
|
||||
```
|
||||
|
||||
|
||||
# Paraglide i18n
|
||||
|
||||
This add-on wires up ParaglideJS for localized routing and message formatting.
|
||||
|
||||
- Messages live in `project.inlang/messages`.
|
||||
- URLs are localized through the Paraglide Vite plugin and router `rewrite` hooks.
|
||||
- Run the dev server or build to regenerate the `src/paraglide` outputs.
|
||||
|
||||
|
||||
|
||||
## Routing
|
||||
|
||||
This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.
|
||||
|
||||
### Adding A Route
|
||||
|
||||
To add a new route to your application just add a new file in the `./src/routes` directory.
|
||||
|
||||
TanStack will automatically generate the content of the route file for you.
|
||||
|
||||
Now that you have two routes you can use a `Link` component to navigate between them.
|
||||
|
||||
### Adding Links
|
||||
|
||||
To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
|
||||
|
||||
```tsx
|
||||
import { Link } from "@tanstack/react-router";
|
||||
```
|
||||
|
||||
Then anywhere in your JSX you can use it like so:
|
||||
|
||||
```tsx
|
||||
<Link to="/about">About</Link>
|
||||
```
|
||||
|
||||
This will create a link that will navigate to the `/about` route.
|
||||
|
||||
More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
|
||||
|
||||
### Using A Layout
|
||||
|
||||
In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you render `{children}` in the `shellComponent`.
|
||||
|
||||
Here is an example layout that includes a header:
|
||||
|
||||
```tsx
|
||||
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ charSet: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
{ title: 'My App' },
|
||||
],
|
||||
}),
|
||||
shellComponent: ({ children }) => (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
<Link to="/">Home</Link>
|
||||
<Link to="/about">About</Link>
|
||||
</nav>
|
||||
</header>
|
||||
{children}
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
),
|
||||
})
|
||||
```
|
||||
|
||||
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
|
||||
|
||||
## Server Functions
|
||||
|
||||
TanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components.
|
||||
|
||||
```tsx
|
||||
import { createServerFn } from '@tanstack/react-start'
|
||||
|
||||
const getServerTime = createServerFn({
|
||||
method: 'GET',
|
||||
}).handler(async () => {
|
||||
return new Date().toISOString()
|
||||
})
|
||||
|
||||
// Use in a component
|
||||
function MyComponent() {
|
||||
const [time, setTime] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
getServerTime().then(setTime)
|
||||
}, [])
|
||||
|
||||
return <div>Server time: {time}</div>
|
||||
}
|
||||
```
|
||||
|
||||
## API Routes
|
||||
|
||||
You can create API routes by using the `server` property in your route definitions:
|
||||
|
||||
```tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { json } from '@tanstack/react-start'
|
||||
|
||||
export const Route = createFileRoute('/api/hello')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: () => json({ message: 'Hello, World!' }),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Data Fetching
|
||||
|
||||
There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
|
||||
|
||||
For example:
|
||||
|
||||
```tsx
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/people')({
|
||||
loader: async () => {
|
||||
const response = await fetch('https://swapi.dev/api/people')
|
||||
return response.json()
|
||||
},
|
||||
component: PeopleComponent,
|
||||
})
|
||||
|
||||
function PeopleComponent() {
|
||||
const data = Route.useLoaderData()
|
||||
return (
|
||||
<ul>
|
||||
{data.results.map((person) => (
|
||||
<li key={person.name}>{person.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
|
||||
|
||||
# Demo files
|
||||
|
||||
Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
|
||||
|
||||
# Learn More
|
||||
|
||||
You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
|
||||
|
||||
For TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start).
|
||||
62
package.json
62
package.json
@@ -14,45 +14,45 @@
|
||||
"machine-translate": "inlang machine translate --project project.inlang"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "^3.0.0-rc.1",
|
||||
"@heroui/styles": "^3.0.0-rc.1",
|
||||
"@heroui/react": "^3.0.5",
|
||||
"@heroui/styles": "^3.0.5",
|
||||
"@sentry/tanstackstart-react": "^10.45.0",
|
||||
"@supabase/ssr": "^0.9.0",
|
||||
"@supabase/supabase-js": "^2.99.3",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@tanstack/react-query": "^5.91.2",
|
||||
"@tanstack/react-router": "^1.167.5",
|
||||
"@tanstack/react-router-ssr-query": "^1.166.9",
|
||||
"@tanstack/react-start": "^1.166.17",
|
||||
"@tanstack/router-plugin": "^1.166.14",
|
||||
"@supabase/ssr": "^0.10.3",
|
||||
"@supabase/supabase-js": "^2.106.1",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"@tanstack/react-query": "^5.100.11",
|
||||
"@tanstack/react-router": "^1.170.7",
|
||||
"@tanstack/react-router-ssr-query": "^1.167.0",
|
||||
"@tanstack/react-start": "^1.168.10",
|
||||
"@tanstack/router-plugin": "^1.168.10",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.577.0",
|
||||
"maplibre-gl": "^5.20.2",
|
||||
"nitro": "^3.0.1-alpha.2",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"lucide-react": "^1.16.0",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"nitro": "^3.0.260522-beta",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"zod": "^4.3.6"
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.4.8",
|
||||
"@inlang/paraglide-js": "^2.15.0",
|
||||
"@tanstack/devtools-vite": "^0.6.0",
|
||||
"@tanstack/react-devtools": "^0.10.0",
|
||||
"@tanstack/react-query-devtools": "^5.91.3",
|
||||
"@tanstack/react-router-devtools": "^1.166.9",
|
||||
"@biomejs/biome": "^2.4.15",
|
||||
"@inlang/paraglide-js": "^2.18.1",
|
||||
"@tanstack/devtools-vite": "^0.7.0",
|
||||
"@tanstack/react-devtools": "^0.10.5",
|
||||
"@tanstack/react-query-devtools": "^5.100.11",
|
||||
"@tanstack/react-router-devtools": "^1.167.0",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/node": "^22.19.15",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/node": "^22.19.19",
|
||||
"@types/react": "^19.2.15",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"jsdom": "^29.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^6.0.2",
|
||||
"jsdom": "^29.1.1",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.0.14",
|
||||
"vite-tsconfig-paths": "^6.1.1",
|
||||
"vitest": "^4.1.0"
|
||||
"vitest": "^4.1.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ export function getSupabaseServerClient() {
|
||||
}))
|
||||
},
|
||||
setAll(cookies) {
|
||||
cookies.forEach((cookie) => {
|
||||
setCookie(cookie.name, cookie.value)
|
||||
cookies.forEach(({ name, value, options }) => {
|
||||
setCookie(name, value, options)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
45
src/lib/server/middleware.ts
Normal file
45
src/lib/server/middleware.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createMiddleware } from "@tanstack/react-start"
|
||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
|
||||
export const authMiddleware = createMiddleware().server(async ({ next }) => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { data, error } = await supabase.auth.getUser()
|
||||
|
||||
if (error || !data.user) {
|
||||
throw new Error("Unauthorized")
|
||||
}
|
||||
|
||||
return next({
|
||||
context: {
|
||||
user: {
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
name: data.user.user_metadata?.name || "",
|
||||
location: data.user.user_metadata?.location || ""
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export const optionalAuthMiddleware = createMiddleware().server(
|
||||
async ({ next }) => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { data, error } = await supabase.auth.getUser()
|
||||
|
||||
const userData =
|
||||
!error && data.user
|
||||
? {
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
name: data.user.user_metadata?.name || "",
|
||||
location: data.user.user_metadata?.location || ""
|
||||
}
|
||||
: null
|
||||
|
||||
return next({
|
||||
context: {
|
||||
user: userData
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -4,8 +4,9 @@ import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||
import {
|
||||
loginFormSchema,
|
||||
signupFormSchema,
|
||||
userListParamsSchema,
|
||||
} from "../validation/user"
|
||||
userListParamsSchema
|
||||
} from "@/lib/validation/user"
|
||||
import { authMiddleware, optionalAuthMiddleware } from "./middleware"
|
||||
|
||||
const login = createServerFn({ method: "POST" })
|
||||
.inputValidator(loginFormSchema)
|
||||
@@ -14,38 +15,41 @@ const login = createServerFn({ method: "POST" })
|
||||
|
||||
const login = await supabase.auth.signInWithPassword({
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
password: data.password
|
||||
})
|
||||
|
||||
if (login.error) {
|
||||
return {
|
||||
error: true,
|
||||
message: login.error.message,
|
||||
message: login.error.message
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
error: false,
|
||||
}
|
||||
throw redirect({
|
||||
to: "/dashboard",
|
||||
replace: true
|
||||
})
|
||||
})
|
||||
|
||||
const logout = createServerFn().handler(async () => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
const logout = createServerFn()
|
||||
.middleware([authMiddleware])
|
||||
.handler(async () => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
|
||||
const { error } = await supabase.auth.signOut()
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message,
|
||||
const { error } = await supabase.auth.signOut()
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw redirect({
|
||||
to: "/",
|
||||
viewTransition: true,
|
||||
replace: true,
|
||||
throw redirect({
|
||||
to: "/",
|
||||
viewTransition: true,
|
||||
replace: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const signup = createServerFn({ method: "POST" })
|
||||
.inputValidator(signupFormSchema)
|
||||
@@ -57,41 +61,36 @@ const signup = createServerFn({ method: "POST" })
|
||||
options: {
|
||||
data: {
|
||||
name: data.name,
|
||||
location: data.location,
|
||||
},
|
||||
},
|
||||
location: data.location
|
||||
}
|
||||
}
|
||||
})
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
|
||||
throw redirect({
|
||||
href: data.redirectUrl || "/",
|
||||
href: data.redirectUrl || "/"
|
||||
})
|
||||
})
|
||||
|
||||
const userData = createServerFn().handler(async () => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
const { data, error } = await supabase.auth.getUser()
|
||||
if (error || !data.user) {
|
||||
return {
|
||||
error: true,
|
||||
message: error?.message ?? "Unknown error",
|
||||
const userData = createServerFn()
|
||||
.middleware([optionalAuthMiddleware])
|
||||
.handler(async ({ context }) => {
|
||||
if (!context.user) {
|
||||
return {
|
||||
error: true,
|
||||
message: "Not authenticated"
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
user: {
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
name: data.user.user_metadata.name || "",
|
||||
location: data.user.user_metadata.location || "",
|
||||
},
|
||||
error: false,
|
||||
}
|
||||
})
|
||||
return {
|
||||
user: context.user,
|
||||
error: false
|
||||
}
|
||||
})
|
||||
|
||||
const resendConfirmationEmail = createServerFn({ method: "POST" })
|
||||
.inputValidator(signupFormSchema.pick({ email: true }))
|
||||
@@ -102,33 +101,34 @@ const resendConfirmationEmail = createServerFn({ method: "POST" })
|
||||
if (error) {
|
||||
return {
|
||||
error: true,
|
||||
message: error.message,
|
||||
message: error.message
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
error: false,
|
||||
error: false
|
||||
}
|
||||
})
|
||||
|
||||
const userList = createServerFn()
|
||||
.middleware([authMiddleware])
|
||||
.inputValidator(userListParamsSchema)
|
||||
.handler(async ({ data }) => {
|
||||
const supabase = getSupabaseServerClient()
|
||||
const users = await supabase.auth.admin.listUsers({
|
||||
page: data.page,
|
||||
perPage: data.limit,
|
||||
perPage: data.limit
|
||||
})
|
||||
|
||||
if (users.error) {
|
||||
return {
|
||||
error: true,
|
||||
message: users.error.message,
|
||||
message: users.error.message
|
||||
}
|
||||
}
|
||||
return {
|
||||
users: users.data,
|
||||
error: false,
|
||||
error: false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -138,5 +138,5 @@ export const user = {
|
||||
signup,
|
||||
userData,
|
||||
resendConfirmationEmail,
|
||||
userList,
|
||||
userList
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import appCss from "@/styles/globals.css?url"
|
||||
|
||||
interface MyRouterContext {
|
||||
queryClient: QueryClient
|
||||
user: null
|
||||
user: Awaited<ReturnType<typeof user.userData>>
|
||||
}
|
||||
|
||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { createFileRoute, Link, Outlet } from "@tanstack/react-router"
|
||||
import { createFileRoute, Link, Outlet, redirect } from "@tanstack/react-router"
|
||||
|
||||
export const Route = createFileRoute("/_auth")({
|
||||
beforeLoad: ({ context }) => {
|
||||
if (context.user.error) {
|
||||
throw new Error("Not authenticated")
|
||||
redirect({
|
||||
to: "/access/login",
|
||||
replace: true,
|
||||
throw: true
|
||||
})
|
||||
}
|
||||
},
|
||||
errorComponent: ({ error }) => {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"target": "ES2022",
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
|
||||
@@ -5,20 +5,21 @@ import { tanstackStart } from "@tanstack/react-start/plugin/vite"
|
||||
import viteReact from "@vitejs/plugin-react"
|
||||
import { nitro } from "nitro/vite"
|
||||
import { defineConfig } from "vite"
|
||||
import tsconfigPaths from "vite-tsconfig-paths"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
devtools(),
|
||||
paraglideVitePlugin({
|
||||
project: "./project.inlang",
|
||||
outdir: "./src/paraglide",
|
||||
outdir: "./src/integrations/paraglide",
|
||||
strategy: ["url", "baseLocale"]
|
||||
}),
|
||||
nitro({ rollupConfig: { external: [/^@sentry\//] } }),
|
||||
tsconfigPaths({ projects: ["./tsconfig.json"] }),
|
||||
tailwindcss(),
|
||||
tanstackStart(),
|
||||
viteReact()
|
||||
]
|
||||
],
|
||||
resolve: {
|
||||
tsconfigPaths: true
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user