@codabytez/create-next-template 0.1.0
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/README.md +548 -0
- package/dist/index.js +407 -0
- package/package.json +31 -0
- package/templates/animations/components/motion/fade-in.tsx +28 -0
- package/templates/appwrite/.env.example +3 -0
- package/templates/appwrite/lib/appwrite.ts +11 -0
- package/templates/auth/.env.example +6 -0
- package/templates/auth/app/(auth)/sign-in/[[...sign-in]]/page.tsx +9 -0
- package/templates/auth/app/(auth)/sign-up/[[...sign-up]]/page.tsx +9 -0
- package/templates/auth/middleware.ts +17 -0
- package/templates/base/.env.example +1 -0
- package/templates/base/.husky/pre-commit +19 -0
- package/templates/base/.husky/pre-push +11 -0
- package/templates/base/.prettierignore +8 -0
- package/templates/base/.prettierrc.json +9 -0
- package/templates/base/CONTRIBUTING.md +120 -0
- package/templates/base/app/globals.css +28 -0
- package/templates/base/app/page.tsx +7 -0
- package/templates/base/components/ui/button.tsx +40 -0
- package/templates/base/eslint.config.mjs +51 -0
- package/templates/base/lib/env.ts +7 -0
- package/templates/base/lib/utils.ts +6 -0
- package/templates/base/next.config.ts +5 -0
- package/templates/base/postcss.config.mjs +7 -0
- package/templates/base/tsconfig.json +23 -0
- package/templates/convex/.env.example +1 -0
- package/templates/convex/convex/schema.ts +10 -0
- package/templates/convex/convex/users.ts +19 -0
- package/templates/convex/providers/convex-provider.tsx +15 -0
- package/templates/data-fetching/lib/api.ts +18 -0
- package/templates/data-fetching/providers/query-provider.tsx +22 -0
- package/templates/forms/components/forms/example-form.tsx +58 -0
- package/templates/forms/lib/validations/example.ts +8 -0
- package/templates/prisma/.env.example +1 -0
- package/templates/prisma/lib/db.ts +11 -0
- package/templates/prisma/prisma/schema.prisma +16 -0
- package/templates/ui/components.json +20 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Contributing Guide
|
|
2
|
+
|
|
3
|
+
## Branching Strategy
|
|
4
|
+
|
|
5
|
+
### Main Branches
|
|
6
|
+
|
|
7
|
+
| Branch | Purpose |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `main` | Production — protected, requires 2 approvals |
|
|
10
|
+
| `staging` | Pre-production testing — protected, requires 1 approval |
|
|
11
|
+
| `dev` | Active development — protected, status checks required |
|
|
12
|
+
|
|
13
|
+
### Working Branches
|
|
14
|
+
|
|
15
|
+
| Prefix | Use for |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `feature/*` | New features |
|
|
18
|
+
| `fix/*` | Bug fixes |
|
|
19
|
+
| `hotfix/*` | Urgent production fixes |
|
|
20
|
+
| `chore/*` | Maintenance / dependency updates |
|
|
21
|
+
| `docs/*` | Documentation only changes |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 1. Pull latest from dev
|
|
29
|
+
git checkout dev
|
|
30
|
+
git pull origin dev
|
|
31
|
+
|
|
32
|
+
# 2. Create your branch
|
|
33
|
+
git checkout -b feature/your-feature-name
|
|
34
|
+
|
|
35
|
+
# 3. Work, commit often
|
|
36
|
+
git add .
|
|
37
|
+
git commit -m "feat: add your feature"
|
|
38
|
+
# pre-commit hook will run automatically:
|
|
39
|
+
# ✔ Prettier format check (auto-fixes if needed)
|
|
40
|
+
# ✔ ESLint (via lint-staged)
|
|
41
|
+
# ✔ TypeScript type check
|
|
42
|
+
|
|
43
|
+
# 4. Push and open a PR into dev
|
|
44
|
+
git push origin feature/your-feature-name
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**PR flow:** `feature/*` → `dev` → `staging` → `main`
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Commit Message Format
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
<type>: <short description>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Type | When to use |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `feat` | New feature |
|
|
60
|
+
| `fix` | Bug fix |
|
|
61
|
+
| `refactor` | Code change that is not a feature or fix |
|
|
62
|
+
| `style` | UI / styling changes |
|
|
63
|
+
| `docs` | Documentation only |
|
|
64
|
+
| `chore` | Maintenance, deps, config |
|
|
65
|
+
| `test` | Adding or updating tests |
|
|
66
|
+
|
|
67
|
+
**Examples:**
|
|
68
|
+
```
|
|
69
|
+
feat: add user profile page
|
|
70
|
+
fix: correct date formatting on dashboard
|
|
71
|
+
chore: upgrade tailwindcss to v4
|
|
72
|
+
docs: update contributing guide
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Git Hooks
|
|
78
|
+
|
|
79
|
+
This project uses [Husky](https://typicode.github.io/husky/) to enforce quality automatically.
|
|
80
|
+
|
|
81
|
+
### Pre-commit
|
|
82
|
+
Runs on every `git commit`:
|
|
83
|
+
- Prettier format check (auto-stages fixes if found)
|
|
84
|
+
- ESLint + Prettier via lint-staged (only on staged files)
|
|
85
|
+
- TypeScript type check
|
|
86
|
+
|
|
87
|
+
### Pre-push
|
|
88
|
+
Runs on every `git push`:
|
|
89
|
+
- Full `next build` — push is blocked if the build fails
|
|
90
|
+
|
|
91
|
+
If a hook blocks you, fix the issue it reports and try again. **Do not bypass hooks with `--no-verify`.**
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Code Standards
|
|
96
|
+
|
|
97
|
+
- **No `console.log()`** — use `console.warn()` or `console.error()` only
|
|
98
|
+
- **No `var`** — always use `const` or `let`
|
|
99
|
+
- **No `any`** — avoid unless absolutely necessary (triggers a warning)
|
|
100
|
+
- **Self-closing JSX** — `<Component />` not `<Component></Component>`
|
|
101
|
+
- **No unnecessary curly braces in JSX** — `title="Hello"` not `title={"Hello"}`
|
|
102
|
+
- **Unused variables** — prefix with `_` if intentionally unused (e.g. `_unused`)
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Code Review Checklist
|
|
107
|
+
|
|
108
|
+
Before requesting a review, make sure:
|
|
109
|
+
|
|
110
|
+
- [ ] All hooks pass (`pnpm lint`, `pnpm check-types`, `pnpm build`)
|
|
111
|
+
- [ ] No `console.log` left in the code
|
|
112
|
+
- [ ] New env variables are added to `.env.example`
|
|
113
|
+
- [ ] UI changes are tested on mobile viewport
|
|
114
|
+
- [ ] No commented-out code committed
|
|
115
|
+
|
|
116
|
+
Reviewers will check:
|
|
117
|
+
- [ ] Logic correctness
|
|
118
|
+
- [ ] TypeScript types are accurate (no sneaky `as any`)
|
|
119
|
+
- [ ] No unnecessary re-renders or missing `useCallback`/`useMemo`
|
|
120
|
+
- [ ] Consistent naming with the rest of the codebase
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
/* ================================
|
|
5
|
+
* Colors — add your custom palette here
|
|
6
|
+
* ================================ */
|
|
7
|
+
|
|
8
|
+
/* --color-primary: #your-color;
|
|
9
|
+
--color-secondary: #your-color;
|
|
10
|
+
--color-accent: #your-color;
|
|
11
|
+
--color-background: #your-color;
|
|
12
|
+
--color-foreground: #your-color; */
|
|
13
|
+
|
|
14
|
+
/* ================================
|
|
15
|
+
* Typography
|
|
16
|
+
* ================================ */
|
|
17
|
+
|
|
18
|
+
/* --font-sans: "Inter", sans-serif;
|
|
19
|
+
--font-mono: "JetBrains Mono", monospace; */
|
|
20
|
+
|
|
21
|
+
/* ================================
|
|
22
|
+
* Spacing / Sizing overrides
|
|
23
|
+
* ================================ */
|
|
24
|
+
|
|
25
|
+
/* --radius-sm: 4px;
|
|
26
|
+
--radius-md: 8px;
|
|
27
|
+
--radius-lg: 12px; */
|
|
28
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
const buttonVariants = cva(
|
|
5
|
+
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "bg-black text-white hover:bg-black/90",
|
|
10
|
+
outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
11
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
12
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
13
|
+
destructive: "bg-red-500 text-white hover:bg-red-600",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "h-10 px-4 py-2",
|
|
17
|
+
sm: "h-9 rounded-md px-3",
|
|
18
|
+
lg: "h-11 rounded-md px-8",
|
|
19
|
+
icon: "h-10 w-10",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export interface ButtonProps
|
|
30
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
31
|
+
VariantProps<typeof buttonVariants> {}
|
|
32
|
+
|
|
33
|
+
export function Button({ className, variant, size, ...props }: ButtonProps) {
|
|
34
|
+
return (
|
|
35
|
+
<button
|
|
36
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { dirname } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
4
|
+
import prettier from "eslint-plugin-prettier/recommended";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const compat = new FlatCompat({ baseDirectory: __dirname });
|
|
10
|
+
|
|
11
|
+
const eslintConfig = [
|
|
12
|
+
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
13
|
+
prettier,
|
|
14
|
+
{
|
|
15
|
+
rules: {
|
|
16
|
+
"prettier/prettier": "error",
|
|
17
|
+
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
18
|
+
"no-debugger": "error",
|
|
19
|
+
"@typescript-eslint/no-unused-vars": [
|
|
20
|
+
"error",
|
|
21
|
+
{
|
|
22
|
+
argsIgnorePattern: "^_",
|
|
23
|
+
varsIgnorePattern: "^_",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
27
|
+
"no-var": "error",
|
|
28
|
+
"prefer-const": "error",
|
|
29
|
+
"prefer-arrow-callback": "error",
|
|
30
|
+
"no-duplicate-imports": "error",
|
|
31
|
+
"react/self-closing-comp": "error",
|
|
32
|
+
"react/jsx-curly-brace-presence": [
|
|
33
|
+
"error",
|
|
34
|
+
{ props: "never", children: "never" },
|
|
35
|
+
],
|
|
36
|
+
"react-hooks/rules-of-hooks": "error",
|
|
37
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
ignores: [
|
|
42
|
+
".next/**",
|
|
43
|
+
"out/**",
|
|
44
|
+
"build/**",
|
|
45
|
+
"next-env.d.ts",
|
|
46
|
+
"node_modules/**",
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules"]
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
export default defineSchema({
|
|
5
|
+
users: defineTable({
|
|
6
|
+
name: v.string(),
|
|
7
|
+
email: v.string(),
|
|
8
|
+
clerkId: v.optional(v.string()),
|
|
9
|
+
}).index("by_email", ["email"]),
|
|
10
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { query, mutation } from "./_generated/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
export const getUsers = query({
|
|
5
|
+
args: {},
|
|
6
|
+
handler: async (ctx) => {
|
|
7
|
+
return await ctx.db.query("users").collect();
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const createUser = mutation({
|
|
12
|
+
args: {
|
|
13
|
+
name: v.string(),
|
|
14
|
+
email: v.string(),
|
|
15
|
+
},
|
|
16
|
+
handler: async (ctx, args) => {
|
|
17
|
+
return await ctx.db.insert("users", args);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
4
|
+
|
|
5
|
+
const convex = new ConvexReactClient(
|
|
6
|
+
process.env.NEXT_PUBLIC_CONVEX_URL as string
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export function ConvexClientProvider({
|
|
10
|
+
children,
|
|
11
|
+
}: {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}) {
|
|
14
|
+
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
export const api = axios.create({
|
|
4
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL ?? "/api",
|
|
5
|
+
headers: {
|
|
6
|
+
"Content-Type": "application/json",
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
api.interceptors.response.use(
|
|
11
|
+
(response) => response,
|
|
12
|
+
(error) => {
|
|
13
|
+
if (error.response?.status === 401) {
|
|
14
|
+
// Handle unauthorized — redirect or clear session
|
|
15
|
+
}
|
|
16
|
+
return Promise.reject(error);
|
|
17
|
+
}
|
|
18
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
|
|
6
|
+
export function QueryProvider({ children }: { children: React.ReactNode }) {
|
|
7
|
+
const [queryClient] = useState(
|
|
8
|
+
() =>
|
|
9
|
+
new QueryClient({
|
|
10
|
+
defaultOptions: {
|
|
11
|
+
queries: {
|
|
12
|
+
staleTime: 60 * 1000,
|
|
13
|
+
retry: 1,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useForm } from "react-hook-form";
|
|
4
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
5
|
+
import { exampleSchema, type ExampleFormValues } from "@/lib/validations/example";
|
|
6
|
+
|
|
7
|
+
export function ExampleForm() {
|
|
8
|
+
const {
|
|
9
|
+
register,
|
|
10
|
+
handleSubmit,
|
|
11
|
+
formState: { errors, isSubmitting },
|
|
12
|
+
} = useForm<ExampleFormValues>({
|
|
13
|
+
resolver: zodResolver(exampleSchema),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
async function onSubmit(data: ExampleFormValues) {
|
|
17
|
+
console.log(data);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4">
|
|
22
|
+
<div className="flex flex-col gap-1">
|
|
23
|
+
<label htmlFor="name" className="text-sm font-medium">Name</label>
|
|
24
|
+
<input
|
|
25
|
+
id="name"
|
|
26
|
+
{...register("name")}
|
|
27
|
+
className="rounded-md border px-3 py-2 text-sm"
|
|
28
|
+
placeholder="Your name"
|
|
29
|
+
/>
|
|
30
|
+
{errors.name && (
|
|
31
|
+
<p className="text-xs text-red-500">{errors.name.message}</p>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="flex flex-col gap-1">
|
|
36
|
+
<label htmlFor="email" className="text-sm font-medium">Email</label>
|
|
37
|
+
<input
|
|
38
|
+
id="email"
|
|
39
|
+
type="email"
|
|
40
|
+
{...register("email")}
|
|
41
|
+
className="rounded-md border px-3 py-2 text-sm"
|
|
42
|
+
placeholder="you@example.com"
|
|
43
|
+
/>
|
|
44
|
+
{errors.email && (
|
|
45
|
+
<p className="text-xs text-red-500">{errors.email.message}</p>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<button
|
|
50
|
+
type="submit"
|
|
51
|
+
disabled={isSubmitting}
|
|
52
|
+
className="rounded-md bg-black px-4 py-2 text-sm text-white hover:bg-black/90 disabled:opacity-50"
|
|
53
|
+
>
|
|
54
|
+
{isSubmitting ? "Submitting..." : "Submit"}
|
|
55
|
+
</button>
|
|
56
|
+
</form>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
|
|
4
|
+
|
|
5
|
+
export const db =
|
|
6
|
+
globalForPrisma.prisma ??
|
|
7
|
+
new PrismaClient({
|
|
8
|
+
log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model User {
|
|
11
|
+
id String @id @default(cuid())
|
|
12
|
+
email String @unique
|
|
13
|
+
name String?
|
|
14
|
+
createdAt DateTime @default(now())
|
|
15
|
+
updatedAt DateTime @updatedAt
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "default",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"hooks": "@/hooks"
|
|
19
|
+
}
|
|
20
|
+
}
|