create-top-secret-starter 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +117 -0
- package/package.json +33 -0
- package/templates/template-router/.env.example +1 -0
- package/templates/template-router/.oxfmtrc.json +13 -0
- package/templates/template-router/.oxlintrc.json +8 -0
- package/templates/template-router/_gitignore +39 -0
- package/templates/template-router/components.json +25 -0
- package/templates/template-router/index.html +24 -0
- package/templates/template-router/package.json +58 -0
- package/templates/template-router/public/favicon.svg +1 -0
- package/templates/template-router/src/api/auth/auth.test.ts +46 -0
- package/templates/template-router/src/api/auth/guards.ts +18 -0
- package/templates/template-router/src/api/auth/index.ts +46 -0
- package/templates/template-router/src/api/auth/refresh.test.ts +89 -0
- package/templates/template-router/src/api/auth/router-bridge.test.ts +74 -0
- package/templates/template-router/src/api/auth/router-bridge.ts +26 -0
- package/templates/template-router/src/api/auth/schema.ts +20 -0
- package/templates/template-router/src/api/auth/session-store.test.ts +101 -0
- package/templates/template-router/src/api/auth/session-store.ts +56 -0
- package/templates/template-router/src/api/index.ts +51 -0
- package/templates/template-router/src/components/status-page.tsx +68 -0
- package/templates/template-router/src/components/ui/button.tsx +58 -0
- package/templates/template-router/src/components/ui/dialog.tsx +136 -0
- package/templates/template-router/src/components/ui/empty.tsx +94 -0
- package/templates/template-router/src/components/ui/field.tsx +222 -0
- package/templates/template-router/src/components/ui/input.tsx +20 -0
- package/templates/template-router/src/components/ui/label.tsx +18 -0
- package/templates/template-router/src/components/ui/select.tsx +188 -0
- package/templates/template-router/src/components/ui/separator.tsx +21 -0
- package/templates/template-router/src/components/ui/skeleton.tsx +13 -0
- package/templates/template-router/src/components/ui/sonner.tsx +43 -0
- package/templates/template-router/src/components/ui/tooltip.tsx +52 -0
- package/templates/template-router/src/env.ts +20 -0
- package/templates/template-router/src/index.css +134 -0
- package/templates/template-router/src/lib/query-client.test.ts +82 -0
- package/templates/template-router/src/lib/query-client.ts +30 -0
- package/templates/template-router/src/lib/single-flight.test.ts +48 -0
- package/templates/template-router/src/lib/single-flight.ts +9 -0
- package/templates/template-router/src/lib/utils.ts +1 -0
- package/templates/template-router/src/main.tsx +26 -0
- package/templates/template-router/src/mocks/mock-server.ts +149 -0
- package/templates/template-router/src/providers/index.tsx +27 -0
- package/templates/template-router/src/providers/theme.test.tsx +68 -0
- package/templates/template-router/src/providers/theme.tsx +76 -0
- package/templates/template-router/src/routeTree.gen.ts +102 -0
- package/templates/template-router/src/router.tsx +29 -0
- package/templates/template-router/src/routes/-error.tsx +34 -0
- package/templates/template-router/src/routes/-not-found.tsx +27 -0
- package/templates/template-router/src/routes/__root.tsx +15 -0
- package/templates/template-router/src/routes/_authenticated/index.tsx +9 -0
- package/templates/template-router/src/routes/_authenticated.tsx +38 -0
- package/templates/template-router/src/routes/sign-in.tsx +100 -0
- package/templates/template-router/src/test/setup.ts +5 -0
- package/templates/template-router/tsconfig.app.json +29 -0
- package/templates/template-router/tsconfig.json +7 -0
- package/templates/template-router/tsconfig.node.json +23 -0
- package/templates/template-router/vite.config.ts +33 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as AuthenticatedRouteImport } from './routes/_authenticated'
|
|
13
|
+
import { Route as SignInRouteImport } from './routes/sign-in'
|
|
14
|
+
import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index'
|
|
15
|
+
|
|
16
|
+
const AuthenticatedRoute = AuthenticatedRouteImport.update({
|
|
17
|
+
id: '/_authenticated',
|
|
18
|
+
getParentRoute: () => rootRouteImport,
|
|
19
|
+
} as any)
|
|
20
|
+
const SignInRoute = SignInRouteImport.update({
|
|
21
|
+
id: '/sign-in',
|
|
22
|
+
path: '/sign-in',
|
|
23
|
+
getParentRoute: () => rootRouteImport,
|
|
24
|
+
} as any)
|
|
25
|
+
const AuthenticatedIndexRoute = AuthenticatedIndexRouteImport.update({
|
|
26
|
+
id: '/',
|
|
27
|
+
path: '/',
|
|
28
|
+
getParentRoute: () => AuthenticatedRoute,
|
|
29
|
+
} as any)
|
|
30
|
+
|
|
31
|
+
export interface FileRoutesByFullPath {
|
|
32
|
+
'/': typeof AuthenticatedIndexRoute
|
|
33
|
+
'/sign-in': typeof SignInRoute
|
|
34
|
+
}
|
|
35
|
+
export interface FileRoutesByTo {
|
|
36
|
+
'/sign-in': typeof SignInRoute
|
|
37
|
+
'/': typeof AuthenticatedIndexRoute
|
|
38
|
+
}
|
|
39
|
+
export interface FileRoutesById {
|
|
40
|
+
__root__: typeof rootRouteImport
|
|
41
|
+
'/_authenticated': typeof AuthenticatedRouteWithChildren
|
|
42
|
+
'/sign-in': typeof SignInRoute
|
|
43
|
+
'/_authenticated/': typeof AuthenticatedIndexRoute
|
|
44
|
+
}
|
|
45
|
+
export interface FileRouteTypes {
|
|
46
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
47
|
+
fullPaths: '/' | '/sign-in'
|
|
48
|
+
fileRoutesByTo: FileRoutesByTo
|
|
49
|
+
to: '/sign-in' | '/'
|
|
50
|
+
id: '__root__' | '/_authenticated' | '/sign-in' | '/_authenticated/'
|
|
51
|
+
fileRoutesById: FileRoutesById
|
|
52
|
+
}
|
|
53
|
+
export interface RootRouteChildren {
|
|
54
|
+
AuthenticatedRoute: typeof AuthenticatedRouteWithChildren
|
|
55
|
+
SignInRoute: typeof SignInRoute
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare module '@tanstack/react-router' {
|
|
59
|
+
interface FileRoutesByPath {
|
|
60
|
+
'/_authenticated': {
|
|
61
|
+
id: '/_authenticated'
|
|
62
|
+
path: ''
|
|
63
|
+
fullPath: '/'
|
|
64
|
+
preLoaderRoute: typeof AuthenticatedRouteImport
|
|
65
|
+
parentRoute: typeof rootRouteImport
|
|
66
|
+
}
|
|
67
|
+
'/sign-in': {
|
|
68
|
+
id: '/sign-in'
|
|
69
|
+
path: '/sign-in'
|
|
70
|
+
fullPath: '/sign-in'
|
|
71
|
+
preLoaderRoute: typeof SignInRouteImport
|
|
72
|
+
parentRoute: typeof rootRouteImport
|
|
73
|
+
}
|
|
74
|
+
'/_authenticated/': {
|
|
75
|
+
id: '/_authenticated/'
|
|
76
|
+
path: '/'
|
|
77
|
+
fullPath: '/'
|
|
78
|
+
preLoaderRoute: typeof AuthenticatedIndexRouteImport
|
|
79
|
+
parentRoute: typeof AuthenticatedRoute
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface AuthenticatedRouteChildren {
|
|
85
|
+
AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const AuthenticatedRouteChildren: AuthenticatedRouteChildren = {
|
|
89
|
+
AuthenticatedIndexRoute: AuthenticatedIndexRoute,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const AuthenticatedRouteWithChildren = AuthenticatedRoute._addFileChildren(
|
|
93
|
+
AuthenticatedRouteChildren,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
97
|
+
AuthenticatedRoute: AuthenticatedRouteWithChildren,
|
|
98
|
+
SignInRoute: SignInRoute,
|
|
99
|
+
}
|
|
100
|
+
export const routeTree = rootRouteImport
|
|
101
|
+
._addFileChildren(rootRouteChildren)
|
|
102
|
+
._addFileTypes<FileRouteTypes>()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createRouter } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
import { bindSessionToRouter } from '@/api/auth/router-bridge'
|
|
4
|
+
import { ErrorPage } from '@/routes/-error'
|
|
5
|
+
import { NotFoundPage } from '@/routes/-not-found'
|
|
6
|
+
import { routeTree } from './routeTree.gen'
|
|
7
|
+
|
|
8
|
+
import { queryClient } from './lib/query-client'
|
|
9
|
+
|
|
10
|
+
export const router = createRouter({
|
|
11
|
+
routeTree,
|
|
12
|
+
defaultPreload: 'intent',
|
|
13
|
+
context: { queryClient },
|
|
14
|
+
scrollRestoration: true,
|
|
15
|
+
defaultStructuralSharing: true,
|
|
16
|
+
defaultPreloadStaleTime: 5_000,
|
|
17
|
+
defaultNotFoundComponent: NotFoundPage,
|
|
18
|
+
defaultErrorComponent: ErrorPage
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Why the router reacts to session changes is auth knowledge; it lives with the
|
|
22
|
+
// rest of the session policy in src/api/auth/router-bridge.ts.
|
|
23
|
+
bindSessionToRouter(router)
|
|
24
|
+
|
|
25
|
+
declare module '@tanstack/react-router' {
|
|
26
|
+
interface Register {
|
|
27
|
+
router: typeof router
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ErrorComponentProps } from '@tanstack/react-router'
|
|
2
|
+
import { RotateCcw, TriangleAlert } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
import { BackHomeButton, StatusPage } from '@/components/status-page'
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
|
|
7
|
+
export const ErrorPage = ({ error, reset }: ErrorComponentProps) => {
|
|
8
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<StatusPage
|
|
12
|
+
code='500'
|
|
13
|
+
icon={<TriangleAlert />}
|
|
14
|
+
mediaClassName='bg-destructive/10 text-destructive'
|
|
15
|
+
title='Something went wrong'
|
|
16
|
+
description='An unexpected error occurred. You can try again, or head back home.'
|
|
17
|
+
actions={
|
|
18
|
+
<>
|
|
19
|
+
<BackHomeButton variant='outline' />
|
|
20
|
+
<Button onClick={() => reset()}>
|
|
21
|
+
<RotateCcw data-icon='inline-start' />
|
|
22
|
+
Try again
|
|
23
|
+
</Button>
|
|
24
|
+
</>
|
|
25
|
+
}
|
|
26
|
+
>
|
|
27
|
+
{import.meta.env.DEV && message && (
|
|
28
|
+
<pre className='text-muted-foreground bg-muted mt-2 max-h-40 w-full overflow-auto rounded-lg border p-3 text-left font-mono text-xs whitespace-pre-wrap'>
|
|
29
|
+
{message}
|
|
30
|
+
</pre>
|
|
31
|
+
)}
|
|
32
|
+
</StatusPage>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useRouter } from '@tanstack/react-router'
|
|
2
|
+
import { ArrowLeft, SearchX } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
import { BackHomeButton, StatusPage } from '@/components/status-page'
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
|
|
7
|
+
export const NotFoundPage = () => {
|
|
8
|
+
const router = useRouter()
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<StatusPage
|
|
12
|
+
code='404'
|
|
13
|
+
icon={<SearchX />}
|
|
14
|
+
title='Page not found'
|
|
15
|
+
description='The page you’re looking for doesn’t exist or may have been moved.'
|
|
16
|
+
actions={
|
|
17
|
+
<>
|
|
18
|
+
<Button variant='outline' onClick={() => router.history.back()}>
|
|
19
|
+
<ArrowLeft data-icon='inline-start' />
|
|
20
|
+
Go back
|
|
21
|
+
</Button>
|
|
22
|
+
<BackHomeButton />
|
|
23
|
+
</>
|
|
24
|
+
}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
import type { QueryClient } from '@tanstack/react-query'
|
|
4
|
+
|
|
5
|
+
export type RouterContext = {
|
|
6
|
+
queryClient: QueryClient
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const RootComponent = () => {
|
|
10
|
+
return <Outlet />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Route = createRootRouteWithContext<RouterContext>()({
|
|
14
|
+
component: RootComponent
|
|
15
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Outlet, createFileRoute, useRouter } from '@tanstack/react-router'
|
|
2
|
+
import { LogOut } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
import { useLogout } from '@/api/auth'
|
|
5
|
+
import { requireSession } from '@/api/auth/guards'
|
|
6
|
+
import { useSession } from '@/api/auth/session-store'
|
|
7
|
+
import { Button } from '@/components/ui/button'
|
|
8
|
+
|
|
9
|
+
export const Route = createFileRoute('/_authenticated')({
|
|
10
|
+
beforeLoad: ({ location }) => requireSession(location),
|
|
11
|
+
component: AuthenticatedLayout
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
function AuthenticatedLayout() {
|
|
15
|
+
const session = useSession()
|
|
16
|
+
const logout = useLogout()
|
|
17
|
+
const router = useRouter()
|
|
18
|
+
|
|
19
|
+
const signOut = async () => {
|
|
20
|
+
await logout.mutateAsync()
|
|
21
|
+
await router.navigate({ to: '/sign-in' })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className='flex min-h-svh flex-col'>
|
|
26
|
+
<header className='flex h-14 items-center justify-between border-b px-6'>
|
|
27
|
+
<span className='text-muted-foreground text-sm'>{session?.user.email}</span>
|
|
28
|
+
<Button variant='outline' size='sm' onClick={signOut} disabled={logout.isPending}>
|
|
29
|
+
<LogOut data-icon='inline-start' />
|
|
30
|
+
Sign out
|
|
31
|
+
</Button>
|
|
32
|
+
</header>
|
|
33
|
+
<main className='flex flex-1 items-center justify-center p-6'>
|
|
34
|
+
<Outlet />
|
|
35
|
+
</main>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
2
|
+
import { createFileRoute, useRouter } from '@tanstack/react-router'
|
|
3
|
+
import { useForm } from 'react-hook-form'
|
|
4
|
+
import * as z from 'zod/mini'
|
|
5
|
+
|
|
6
|
+
import { useLogin, type Credentials } from '@/api/auth'
|
|
7
|
+
import { redirectIfAuthenticated } from '@/api/auth/guards'
|
|
8
|
+
import { Button } from '@/components/ui/button'
|
|
9
|
+
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field'
|
|
10
|
+
import { Input } from '@/components/ui/input'
|
|
11
|
+
|
|
12
|
+
const searchSchema = z.object({
|
|
13
|
+
redirect: z.catch(z.optional(z.string()), undefined)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const formSchema = z.object({
|
|
17
|
+
email: z.string().check(z.minLength(1, 'Email is required'), z.email('Enter a valid email')),
|
|
18
|
+
password: z.string().check(z.minLength(1, 'Password is required'))
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Seeded in src/mocks/mock-server.ts — removed alongside the mock backend.
|
|
22
|
+
// `import.meta.env.DEV` is statically replaced at build time, so the literals are
|
|
23
|
+
// tree-shaken out of production bundles rather than merely unused there.
|
|
24
|
+
const DEMO_CREDENTIALS: Credentials = import.meta.env.DEV
|
|
25
|
+
? { email: 'demo@example.com', password: 'demo1234' }
|
|
26
|
+
: { email: '', password: '' }
|
|
27
|
+
|
|
28
|
+
const FIELDS = [
|
|
29
|
+
{ name: 'email', label: 'Email', type: 'email', autoComplete: 'email' },
|
|
30
|
+
{ name: 'password', label: 'Password', type: 'password', autoComplete: 'current-password' }
|
|
31
|
+
] as const satisfies ReadonlyArray<{
|
|
32
|
+
name: keyof Credentials
|
|
33
|
+
label: string
|
|
34
|
+
type: string
|
|
35
|
+
autoComplete: string
|
|
36
|
+
}>
|
|
37
|
+
|
|
38
|
+
export const Route = createFileRoute('/sign-in')({
|
|
39
|
+
validateSearch: searchSchema,
|
|
40
|
+
beforeLoad: ({ search }) => redirectIfAuthenticated(search),
|
|
41
|
+
component: SignInPage
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
function SignInPage() {
|
|
45
|
+
const router = useRouter()
|
|
46
|
+
const { redirect: redirectTo } = Route.useSearch()
|
|
47
|
+
const login = useLogin()
|
|
48
|
+
|
|
49
|
+
const { register, handleSubmit, formState } = useForm<Credentials>({
|
|
50
|
+
resolver: standardSchemaResolver(formSchema),
|
|
51
|
+
defaultValues: DEMO_CREDENTIALS
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const onSubmit = handleSubmit(async values => {
|
|
55
|
+
await login.mutateAsync(values)
|
|
56
|
+
await router.navigate({ to: redirectTo ?? '/' })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<main className='flex min-h-svh items-center justify-center p-6'>
|
|
61
|
+
<form onSubmit={onSubmit} className='w-full max-w-sm'>
|
|
62
|
+
<FieldGroup>
|
|
63
|
+
<div className='flex flex-col gap-1 text-center'>
|
|
64
|
+
<h1 className='text-xl font-semibold'>Sign in</h1>
|
|
65
|
+
{import.meta.env.DEV && (
|
|
66
|
+
<p className='text-muted-foreground text-sm'>Sign in with the seeded demo account.</p>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{import.meta.env.DEV && (
|
|
71
|
+
<div className='bg-muted text-muted-foreground rounded-lg border px-3 py-2 text-center font-mono text-xs'>
|
|
72
|
+
{DEMO_CREDENTIALS.email} · {DEMO_CREDENTIALS.password}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
{FIELDS.map(field => {
|
|
77
|
+
const error = formState.errors[field.name]
|
|
78
|
+
return (
|
|
79
|
+
<Field key={field.name} data-invalid={Boolean(error)}>
|
|
80
|
+
<FieldLabel htmlFor={field.name}>{field.label}</FieldLabel>
|
|
81
|
+
<Input
|
|
82
|
+
id={field.name}
|
|
83
|
+
type={field.type}
|
|
84
|
+
autoComplete={field.autoComplete}
|
|
85
|
+
aria-invalid={Boolean(error)}
|
|
86
|
+
{...register(field.name)}
|
|
87
|
+
/>
|
|
88
|
+
<FieldError errors={[error]} />
|
|
89
|
+
</Field>
|
|
90
|
+
)
|
|
91
|
+
})}
|
|
92
|
+
|
|
93
|
+
<Button type='submit' disabled={formState.isSubmitting}>
|
|
94
|
+
{formState.isSubmitting ? 'Signing in…' : 'Sign in'}
|
|
95
|
+
</Button>
|
|
96
|
+
</FieldGroup>
|
|
97
|
+
</form>
|
|
98
|
+
</main>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023", "DOM"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["vite/client", "vitest/globals", "vitest/jsdom", "@testing-library/jest-dom"],
|
|
8
|
+
"allowArbitraryExtensions": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
/* Bundler mode */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"moduleDetection": "force",
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
"noImplicitOverride": true,
|
|
19
|
+
"paths": { "@/*": ["./src/*"] },
|
|
20
|
+
|
|
21
|
+
/* Linting */
|
|
22
|
+
"noUnusedLocals": true,
|
|
23
|
+
"noUnusedParameters": true,
|
|
24
|
+
"erasableSyntaxOnly": true,
|
|
25
|
+
"noFallthroughCasesInSwitch": true,
|
|
26
|
+
"noUncheckedIndexedAccess": true
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"]
|
|
29
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"moduleDetection": "force",
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
/* Linting */
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"erasableSyntaxOnly": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["vite.config.ts"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import babel from '@rolldown/plugin-babel'
|
|
2
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
3
|
+
import { tanstackRouter } from '@tanstack/router-plugin/vite'
|
|
4
|
+
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { configDefaults, defineConfig } from 'vitest/config'
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
plugins: [
|
|
15
|
+
tanstackRouter({
|
|
16
|
+
target: 'react',
|
|
17
|
+
autoCodeSplitting: true
|
|
18
|
+
}),
|
|
19
|
+
react(),
|
|
20
|
+
tailwindcss(),
|
|
21
|
+
babel({ presets: [reactCompilerPreset()] })
|
|
22
|
+
],
|
|
23
|
+
test: {
|
|
24
|
+
globals: true,
|
|
25
|
+
environment: 'happy-dom',
|
|
26
|
+
environmentOptions: { happyDOM: { url: 'http://localhost' } },
|
|
27
|
+
env: { VITE_API_URL: 'http://localhost/api/' },
|
|
28
|
+
setupFiles: ['./src/test/setup.ts'],
|
|
29
|
+
restoreMocks: true,
|
|
30
|
+
css: false,
|
|
31
|
+
exclude: [...configDefaults.exclude, '**/e2e/**', 'create-cli/**']
|
|
32
|
+
}
|
|
33
|
+
})
|