create-lve 0.4.18 → 0.4.19

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/config.js CHANGED
@@ -7,40 +7,6 @@ import { execSync, spawn } from 'node:child_process'
7
7
 
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
9
 
10
- const getReactAppTemplate = (isUno) => {
11
- const logoClass = isUno
12
- ? 'animate-spin animate-duration-20s animate-linear animate-infinite'
13
- : 'animate-[spin_20s_linear_infinite]'
14
-
15
- return `
16
- import { useState } from 'react'
17
- import reactLogo from './assets/react.svg'
18
-
19
- export function App() {
20
- const [count, setCount] = useState(0)
21
-
22
- return (
23
- <div className="max-w-7xl mx-auto p-8 text-center font-sans antialiased text-[#213547] dark:text-zinc-200 min-h-dvh flex flex-col justify-center items-center">
24
- <div className="flex justify-center gap-12 mb-12">
25
- <a href="https://viteplus.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#646cffaa]">
26
- <img src="/favicon.svg" className="h-24 p-6" alt="VitePlus logo" />
27
- </a>
28
- <a href="https://react.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#61dafbaa]">
29
- <img src={reactLogo} className="h-24 p-6 ${logoClass}" alt="React logo" />
30
- </a>
31
- </div>
32
- <h1 className="text-5xl font-bold leading-[1.1] mb-8">VitePlus + React</h1>
33
- <div className="p-8 space-y-4 flex flex-col items-center">
34
- <button onClick={() => setCount((count) => count + 1)} className="rounded-lg border border-transparent px-5 py-2.5 text-base font-medium bg-[#f9f9f9] dark:bg-zinc-800 cursor-pointer transition-colors hover:border-[#646cff] outline-none">
35
- count is {count}
36
- </button>
37
- </div>
38
- </div>
39
- )
40
- }
41
- `.trim()
42
- }
43
-
44
10
  const FRAMEWORK_CONFIG = {
45
11
  next: {
46
12
  deps: (isUno) => ({
@@ -50,26 +16,7 @@ const FRAMEWORK_CONFIG = {
50
16
  react: {
51
17
  deps: (isUno) => ({
52
18
  dependencies: isUno ? { '@unocss/reset': 'latest' } : {},
53
- devDependencies: {
54
- 'vite-plus': 'latest',
55
- '@vitejs/plugin-react': 'latest',
56
- '@rolldown/plugin-babel': 'latest',
57
- '@babel/core': 'latest',
58
- 'babel-plugin-react-compiler': 'latest',
59
- '@types/babel__core': 'latest',
60
- typescript: 'latest',
61
-
62
- ...(isUno
63
- ? { unocss: 'latest' }
64
- : { tailwindcss: 'latest', '@tailwindcss/vite': 'latest' }),
65
- },
66
-
67
- pnpm: {
68
- overrides: {
69
- vite: 'npm:@voidzero-dev/vite-plus-core@latest',
70
- vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
71
- },
72
- },
19
+ devDependencies: isUno ? { unocss: 'latest' } : {},
73
20
  }),
74
21
  },
75
22
  vue: {
@@ -79,12 +26,31 @@ const FRAMEWORK_CONFIG = {
79
26
 
80
27
  const CSS_STRATEGIES = {
81
28
  unocss: {
82
- pluginImport: "import UnoCSS from 'unocss/vite'\n",
83
- pluginCode: 'UnoCSS(), ',
84
- entryImport: "import '@unocss/reset/tailwind.css'\nimport 'virtual:uno.css'\n",
29
+ pluginImport: '',
30
+ pluginCode: '',
31
+ entryImport: '',
85
32
  async setup(ctx) {
86
- const unoConfig = `
87
- import {
33
+ // Replace tailwindcss with UnoCSS in vite.config.ts
34
+ const vitePath = path.join(ctx.targetDir, 'vite.config.ts')
35
+ let viteContent = await fs.readFile(vitePath, 'utf-8')
36
+ viteContent = viteContent.replace(
37
+ "import tailwindcss from '@tailwindcss/vite'",
38
+ "import UnoCSS from 'unocss/vite'",
39
+ )
40
+ viteContent = viteContent.replace('tailwindcss(),', 'UnoCSS(),')
41
+ await fs.writeFile(vitePath, viteContent)
42
+
43
+ // Replace style.css import with UnoCSS imports in main.tsx
44
+ const mainPath = path.join(ctx.targetDir, 'src/app/main.tsx')
45
+ let mainContent = await fs.readFile(mainPath, 'utf-8')
46
+ mainContent = mainContent.replace(
47
+ "import '@/style.css'",
48
+ "import '@unocss/reset/tailwind.css'\nimport 'virtual:uno.css'",
49
+ )
50
+ await fs.writeFile(mainPath, mainContent)
51
+
52
+ // Write UnoCSS config
53
+ const unoConfig = `import {
88
54
  defineConfig,
89
55
  presetWind3,
90
56
  transformerCompileClass,
@@ -121,7 +87,6 @@ export default defineConfig({
121
87
  transformerCompileClass({
122
88
  classPrefix: '',
123
89
  hashFn: (str) => {
124
- // return createHash('md5').update(str).digest('hex').slice(0, 8)
125
90
  const hash = createHash('md5').update(str).digest('hex').slice(0, 8)
126
91
  return /^\\d/.test(hash) ? 'v' + hash.slice(1, 8) : hash.slice(0, 8)
127
92
  },
@@ -130,35 +95,19 @@ export default defineConfig({
130
95
  ]
131
96
  : []) as SourceCodeTransformer[],
132
97
  })
98
+ `
99
+ await fs.writeFile(path.join(ctx.targetDir, 'uno.config.ts'), unoConfig)
133
100
 
134
- `.trim()
135
- await fs.writeFile(path.join(ctx.targetDir, 'uno.config.ts'), unoConfig + '\n')
136
-
137
- const tsconfigPath = path.join(ctx.targetDir, 'tsconfig.node.json')
138
- if (fs.existsSync(tsconfigPath)) {
139
- try {
140
- let content = await fs.readFile(tsconfigPath, 'utf-8')
141
-
142
- if (content.includes('/* UNO_CONFIG */')) {
143
- content = content.replace('/* UNO_CONFIG */', ', "uno.config.ts"')
144
- await fs.writeFile(tsconfigPath, content)
145
- }
146
- } catch {}
147
- }
148
-
101
+ // Remove Tailwind style.css
149
102
  const stylePath = path.join(ctx.targetDir, 'src/style.css')
150
103
  if (fs.existsSync(stylePath)) await fs.remove(stylePath)
151
104
  },
152
105
  },
153
106
  tailwind: {
154
- pluginImport: "import tailwindcss from '@tailwindcss/vite'\n",
155
- pluginCode: 'tailwindcss(), ',
156
- entryImport: "import './style.css'\n",
157
- async setup(ctx) {
158
- const stylePath = path.join(ctx.targetDir, 'src/style.css')
159
- await fs.ensureDir(path.dirname(stylePath))
160
- await fs.writeFile(stylePath, `@import "tailwindcss";`)
161
- },
107
+ pluginImport: '',
108
+ pluginCode: '',
109
+ entryImport: '',
110
+ async setup() {},
162
111
  },
163
112
  }
164
113
 
@@ -221,25 +170,6 @@ async function applyProjectTransform(ctx) {
221
170
  if (isNext) return
222
171
 
223
172
  const strategy = CSS_STRATEGIES[css]
224
- const isVue = framework === 'vue'
225
- const paths = {
226
- main: path.join(targetDir, isVue ? 'src/main.ts' : 'src/main.tsx'),
227
- vite: path.join(targetDir, 'vite.config.ts'),
228
- app: path.join(targetDir, isVue ? 'src/App.vue' : 'src/App.tsx'),
229
- }
230
-
231
- if (framework === 'react') {
232
- await fs.writeFile(paths.app, getReactAppTemplate(isUno))
233
- }
234
-
235
- let viteContent = await fs.readFile(paths.vite, 'utf-8')
236
- viteContent = strategy.pluginImport + viteContent
237
- viteContent = viteContent.replace('/* VITE_PLUS_PLUGINS */', strategy.pluginCode)
238
- await fs.writeFile(paths.vite, viteContent)
239
-
240
- let mainContent = await fs.readFile(paths.main, 'utf-8')
241
- await fs.writeFile(paths.main, strategy.entryImport + mainContent)
242
-
243
173
  await strategy.setup(ctx)
244
174
  }
245
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lve",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "bin": {
5
5
  "create-lve": "index.js"
6
6
  },
@@ -35,5 +35,6 @@
35
35
  "oxlint --fix"
36
36
  ],
37
37
  "*.json": "oxfmt --write"
38
- }
38
+ },
39
+ "packageManager": "pnpm@11.5.0"
39
40
  }
@@ -0,0 +1 @@
1
+ VITE_APP_TITLE=MyApp
@@ -0,0 +1 @@
1
+ VITE_API_BASE_URL=
@@ -0,0 +1 @@
1
+ VITE_API_BASE_URL=https://api.example.com
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "radix-lyra",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/style.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "inverted",
23
+ "menuAccent": "bold",
24
+ "registries": {}
25
+ }
@@ -8,6 +8,6 @@
8
8
  </head>
9
9
  <body>
10
10
  <div id="root"></div>
11
- <script type="module" src="/src/main.tsx"></script>
11
+ <script type="module" src="/src/app/main.tsx"></script>
12
12
  </body>
13
13
  </html>
@@ -10,15 +10,26 @@
10
10
  "prepare": "vp config"
11
11
  },
12
12
  "dependencies": {
13
+ "@tanstack/react-query": "latest",
14
+ "class-variance-authority": "latest",
15
+ "clsx": "latest",
16
+ "jotai": "latest",
13
17
  "react": "latest",
14
- "react-dom": "latest"
18
+ "react-dom": "latest",
19
+ "react-router": "latest",
20
+ "tailwind-merge": "latest",
21
+ "zustand": "latest"
15
22
  },
16
23
  "devDependencies": {
24
+ "@babel/core": "latest",
25
+ "@rolldown/plugin-babel": "latest",
26
+ "@tailwindcss/vite": "latest",
17
27
  "@types/node": "latest",
18
28
  "@types/react": "latest",
19
29
  "@types/react-dom": "latest",
20
30
  "@vitejs/plugin-react": "latest",
21
31
  "babel-plugin-react-compiler": "latest",
32
+ "tailwindcss": "latest",
22
33
  "typescript": "latest",
23
34
  "vite": "latest",
24
35
  "vite-plus": "latest"
@@ -0,0 +1,6 @@
1
+ import { RouterProvider } from 'react-router'
2
+ import { router } from './router'
3
+
4
+ export function App() {
5
+ return <RouterProvider router={router} />
6
+ }
@@ -0,0 +1,46 @@
1
+ import '@/style.css'
2
+ import { StrictMode } from 'react'
3
+ import { createRoot } from 'react-dom/client'
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
5
+ import { http } from '@/lib/http'
6
+ import { App } from './App'
7
+
8
+ const queryClient = new QueryClient({
9
+ defaultOptions: {
10
+ queries: {
11
+ staleTime: 5 * 60 * 1000,
12
+ gcTime: 10 * 60 * 1000,
13
+ refetchOnWindowFocus: false,
14
+ retry: 1,
15
+ },
16
+ },
17
+ })
18
+
19
+ // ── 请求拦截:自动注入 token ──
20
+ http.interceptors.request.use((config) => {
21
+ const token = localStorage.getItem('token')
22
+ if (token) {
23
+ config.headers = { ...config.headers, Authorization: `Bearer ${token}` }
24
+ }
25
+ return config
26
+ })
27
+
28
+ // ── 响应拦截:401 跳登录 ──
29
+ http.interceptors.response.use(
30
+ (res) => res,
31
+ (err) => {
32
+ if (err.status === 401) {
33
+ localStorage.removeItem('token')
34
+ window.location.href = '/login'
35
+ }
36
+ throw err
37
+ },
38
+ )
39
+
40
+ createRoot(document.getElementById('root')!).render(
41
+ <StrictMode>
42
+ <QueryClientProvider client={queryClient}>
43
+ <App />
44
+ </QueryClientProvider>
45
+ </StrictMode>,
46
+ )
@@ -0,0 +1,51 @@
1
+ import { createBrowserRouter, Outlet, Navigate } from 'react-router'
2
+
3
+ // ── 懒加载 ──
4
+ const lazyRoute = (loader: () => Promise<{ default: React.ComponentType }>) => ({
5
+ lazy: async () => {
6
+ const { default: Component } = await loader()
7
+ return { Component }
8
+ },
9
+ })
10
+
11
+ // ── 路由守卫 ──
12
+ function ProtectedRoute() {
13
+ // TODO: 替换为你的 useAuthInit hook
14
+ const isLogged = !!localStorage.getItem('token')
15
+
16
+ if (!isLogged) return <Navigate to="/login" replace />
17
+ return <Outlet />
18
+ }
19
+
20
+ // ── 布局 ──
21
+ function RootLayout() {
22
+ return (
23
+ <div className="min-h-dvh bg-white text-neutral-900 font-sans antialiased dark:bg-neutral-950 dark:text-neutral-100">
24
+ <Outlet />
25
+ </div>
26
+ )
27
+ }
28
+
29
+ // ── 路由配置 ──
30
+ export const router = createBrowserRouter([
31
+ {
32
+ path: '/',
33
+ element: <RootLayout />,
34
+ children: [
35
+ {
36
+ index: true,
37
+ ...lazyRoute(() => import('@/features/_example/pages/Example')),
38
+ },
39
+ // TODO: 在这里添加你的路由
40
+ // { path: 'users', ...lazyRoute(() => import('@/features/user/pages/UserList')) },
41
+ // { path: 'users/:id', ...lazyRoute(() => import('@/features/user/pages/UserDetail')) },
42
+ // {
43
+ // path: 'dashboard',
44
+ // element: <ProtectedRoute />,
45
+ // children: [
46
+ // { index: true, ...lazyRoute(() => import('@/features/dashboard/pages/Dashboard')) },
47
+ // ],
48
+ // },
49
+ ],
50
+ },
51
+ ])
File without changes
@@ -0,0 +1,20 @@
1
+ // ── 示例 Hooks ──
2
+ // 编排层:调 services/stores → 处理状态 → 供页面使用
3
+
4
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
5
+ import { exampleService } from '../services/example.service'
6
+
7
+ export function useExampleList(params?: { page?: number; limit?: number }) {
8
+ return useQuery({
9
+ queryKey: ['examples', params],
10
+ queryFn: () => exampleService.getList(params),
11
+ })
12
+ }
13
+
14
+ export function useDeleteExample() {
15
+ const qc = useQueryClient()
16
+ return useMutation({
17
+ mutationFn: (id: string) => exampleService.delete(id),
18
+ onSuccess: () => qc.invalidateQueries({ queryKey: ['examples'] }),
19
+ })
20
+ }
@@ -0,0 +1,14 @@
1
+ export function Example() {
2
+ return (
3
+ <div className="flex flex-col items-center justify-center min-h-dvh gap-4">
4
+ <h1 className="text-2xl font-bold">Ready to build 🚀</h1>
5
+ <p className="text-neutral-500 text-sm">
6
+ Edit{' '}
7
+ <code className="bg-neutral-100 dark:bg-neutral-800 px-1.5 py-0.5 rounded font-mono text-xs">
8
+ src/app/router.tsx
9
+ </code>{' '}
10
+ to add your routes
11
+ </p>
12
+ </div>
13
+ )
14
+ }
@@ -0,0 +1,23 @@
1
+ // ── 示例 Service ──
2
+ // 只发请求,零状态,零 UI
3
+
4
+ import { http } from '@/lib/http'
5
+ import type { ExampleItem, ExampleListResponse } from '../types'
6
+
7
+ export const exampleService = {
8
+ getList(params?: { page?: number; limit?: number }) {
9
+ return http.get<ExampleListResponse>('/api/examples', { params })
10
+ },
11
+
12
+ getById(id: string) {
13
+ return http.get<ExampleItem>(`/api/examples/${id}`)
14
+ },
15
+
16
+ create(data: Omit<ExampleItem, 'id'>) {
17
+ return http.post<ExampleItem>('/api/examples', data)
18
+ },
19
+
20
+ delete(id: string) {
21
+ return http.delete(`/api/examples/${id}`)
22
+ },
23
+ }
@@ -0,0 +1,12 @@
1
+ // ── 示例类型定义 ──
2
+ // 每个 feature 模块的类型放这里,层与层之间通过类型契约通信
3
+
4
+ export interface ExampleItem {
5
+ id: string
6
+ name: string
7
+ }
8
+
9
+ export interface ExampleListResponse {
10
+ data: ExampleItem[]
11
+ total: number
12
+ }
File without changes
@@ -0,0 +1,144 @@
1
+ const BASE_URL = import.meta.env.VITE_API_BASE_URL
2
+
3
+ // ── 类型 ──
4
+
5
+ interface RequestOptions extends Omit<RequestInit, 'method' | 'body'> {
6
+ params?: Record<string, string | number | boolean | undefined | null>
7
+ data?: unknown
8
+ timeout?: number
9
+ }
10
+
11
+ type RequestInterceptor = (
12
+ config: RequestInit & { url: string },
13
+ ) => (RequestInit & { url: string }) | Promise<RequestInit & { url: string }>
14
+
15
+ type ResponseInterceptor = (response: Response) => Response | Promise<Response>
16
+
17
+ interface HttpError extends Error {
18
+ status: number
19
+ data: unknown
20
+ }
21
+
22
+ // ── 拦截器管理 ──
23
+
24
+ const interceptors = {
25
+ request: [] as RequestInterceptor[],
26
+ response: [] as ResponseInterceptor[],
27
+ }
28
+
29
+ // ── 工具函数 ──
30
+
31
+ function buildUrl(
32
+ path: string,
33
+ params?: Record<string, string | number | boolean | undefined | null>,
34
+ ) {
35
+ const isSameOrigin = !BASE_URL || BASE_URL === window.location.origin
36
+ const base = isSameOrigin ? '' : BASE_URL
37
+ const url = new URL(path, base || window.location.origin)
38
+ if (params) {
39
+ Object.entries(params).forEach(([key, value]) => {
40
+ if (value != null) url.searchParams.set(key, String(value))
41
+ })
42
+ }
43
+ return isSameOrigin ? `${url.pathname}${url.search}` : url.toString()
44
+ }
45
+
46
+ async function parseResponse<T>(res: Response): Promise<T> {
47
+ const contentType = res.headers.get('content-type')
48
+ if (contentType?.includes('application/json')) return res.json() as Promise<T>
49
+ return res.text() as unknown as Promise<T>
50
+ }
51
+
52
+ function createHttpError(res: Response, data: unknown): HttpError {
53
+ const err = new Error(`HTTP ${res.status}`) as HttpError
54
+ err.status = res.status
55
+ err.data = data
56
+ return err
57
+ }
58
+
59
+ // ── 核心请求 ──
60
+
61
+ async function request<T = unknown>(
62
+ method: string,
63
+ path: string,
64
+ options: RequestOptions = {},
65
+ ): Promise<T> {
66
+ const { params, data, timeout = 15000, signal, ...rest } = options
67
+
68
+ const controller = new AbortController()
69
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
70
+
71
+ if (signal) {
72
+ signal.addEventListener('abort', () => controller.abort())
73
+ }
74
+
75
+ let config: RequestInit & { url: string } = {
76
+ url: buildUrl(path, params),
77
+ method,
78
+ headers: { 'Content-Type': 'application/json' },
79
+ body: data ? JSON.stringify(data) : undefined,
80
+ signal: controller.signal,
81
+ ...rest,
82
+ }
83
+
84
+ for (const interceptor of interceptors.request) {
85
+ config = await interceptor(config)
86
+ }
87
+
88
+ try {
89
+ const { url, ...fetchConfig } = config
90
+ let res = await fetch(url, fetchConfig)
91
+
92
+ for (const interceptor of interceptors.response) {
93
+ res = await interceptor(res)
94
+ }
95
+
96
+ if (!res.ok) {
97
+ const errorData = await parseResponse<unknown>(res).catch(() => null)
98
+ throw createHttpError(res, errorData)
99
+ }
100
+
101
+ return await parseResponse<T>(res)
102
+ } finally {
103
+ clearTimeout(timeoutId)
104
+ }
105
+ }
106
+
107
+ // ── 导出 ──
108
+
109
+ export const http = {
110
+ request,
111
+
112
+ get<T = unknown>(path: string, options?: RequestOptions) {
113
+ return request<T>('GET', path, options)
114
+ },
115
+
116
+ post<T = unknown>(path: string, data?: unknown, options?: RequestOptions) {
117
+ return request<T>('POST', path, { ...options, data })
118
+ },
119
+
120
+ put<T = unknown>(path: string, data?: unknown, options?: RequestOptions) {
121
+ return request<T>('PUT', path, { ...options, data })
122
+ },
123
+
124
+ patch<T = unknown>(path: string, data?: unknown, options?: RequestOptions) {
125
+ return request<T>('PATCH', path, { ...options, data })
126
+ },
127
+
128
+ delete<T = unknown>(path: string, options?: RequestOptions) {
129
+ return request<T>('DELETE', path, options)
130
+ },
131
+
132
+ interceptors: {
133
+ request: {
134
+ use(interceptor: RequestInterceptor) {
135
+ interceptors.request.push(interceptor)
136
+ },
137
+ },
138
+ response: {
139
+ use(onFulfilled: ResponseInterceptor) {
140
+ interceptors.response.push(onFulfilled)
141
+ },
142
+ },
143
+ },
144
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
File without changes
@@ -0,0 +1 @@
1
+ @import 'tailwindcss';
File without changes
@@ -1,11 +1,10 @@
1
+ import tailwindcss from '@tailwindcss/vite'
1
2
  import { defineConfig, loadEnv, lazyPlugins } from 'vite-plus'
2
3
  import react, { reactCompilerPreset } from '@vitejs/plugin-react'
3
- // import { fileURLToPath, URL } from 'node:url'
4
4
 
5
5
  const mode = process.env.NODE_ENV || 'development'
6
6
  const env = loadEnv(mode, process.cwd(), '')
7
7
 
8
- // https://vite.dev/config/
9
8
  export default defineConfig({
10
9
  fmt: { semi: false, singleQuote: true },
11
10
  staged: {
@@ -13,7 +12,7 @@ export default defineConfig({
13
12
  },
14
13
  lint: { options: { typeAware: true, typeCheck: true } },
15
14
  plugins: [
16
- /* VITE_PLUS_PLUGINS */
15
+ tailwindcss(),
17
16
  react(),
18
17
  lazyPlugins(async () => {
19
18
  const { default: babel } = await import('@rolldown/plugin-babel')
@@ -25,9 +24,6 @@ export default defineConfig({
25
24
  }),
26
25
  ],
27
26
  resolve: {
28
- // alias: {
29
- // '@': fileURLToPath(new URL('./src', import.meta.url)),
30
- // },
31
27
  tsconfigPaths: true,
32
28
  },
33
29
  server: {
@@ -39,4 +35,30 @@ export default defineConfig({
39
35
  },
40
36
  },
41
37
  },
38
+ build: {
39
+ rolldownOptions: {
40
+ output: {
41
+ manualChunks(id) {
42
+ if (!id.includes('node_modules')) return
43
+
44
+ if (id.includes('/react-dom/')) return 'vendor-react-dom'
45
+ if (id.includes('/react/') && !id.includes('react-router')) return 'vendor-react'
46
+
47
+ if (id.includes('react-router')) return 'vendor-router'
48
+ if (id.includes('@tanstack/react-query')) return 'vendor-query'
49
+ if (id.includes('zustand')) return 'vendor-zustand'
50
+ if (id.includes('jotai')) return 'vendor-jotai'
51
+
52
+ if (
53
+ id.includes('tailwind-merge') ||
54
+ id.includes('clsx') ||
55
+ id.includes('class-variance-authority')
56
+ )
57
+ return 'vendor-style-utils'
58
+
59
+ return 'vendor-misc'
60
+ },
61
+ },
62
+ },
63
+ },
42
64
  })
@@ -1,68 +0,0 @@
1
- import { useState } from 'react'
2
- import reactLogo from './assets/react.svg'
3
-
4
- export function App() {
5
- const [count, setCount] = useState(0)
6
-
7
- return (
8
- <div className="max-w-7xl mx-auto p-8 text-center font-sans antialiased text-[#213547] dark:text-zinc-200 min-h-dvh flex flex-col justify-center items-center">
9
- <div className="flex justify-center gap-12 mb-12">
10
- <a
11
- href="https://viteplus.dev"
12
- target="_blank"
13
- rel="noreferrer"
14
- className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#646cffaa]"
15
- >
16
- <img src="/favicon.svg" className="h-24 p-6" alt="VitePlus logo" />
17
- </a>
18
- <a
19
- href="https://react.dev"
20
- target="_blank"
21
- rel="noreferrer"
22
- className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#61dafbaa]"
23
- >
24
- <img
25
- src={reactLogo}
26
- className="h-24 p-6 animate-[spin_20s_linear_infinite]"
27
- alt="React logo"
28
- />
29
- </a>
30
- </div>
31
-
32
- <h1 className="text-5xl font-bold leading-[1.1] mb-8">VitePlus + React</h1>
33
-
34
- <div className="p-8 space-y-4 flex flex-col items-center">
35
- <button
36
- onClick={() => setCount((count) => count + 1)}
37
- className="rounded-lg border border-transparent px-5 py-2.5 text-base font-medium bg-[#f9f9f9] dark:bg-zinc-800 cursor-pointer transition-colors hover:border-[#646cff] outline-none"
38
- >
39
- count is {count}
40
- </button>
41
- <p className="text-zinc-500">
42
- Edit{' '}
43
- <code className="bg-[#f1f1f1] dark:bg-zinc-800 px-1.5 py-0.5 rounded font-mono">
44
- src/App.tsx
45
- </code>{' '}
46
- to test HMR
47
- </p>
48
- </div>
49
-
50
- <p className="text-[#888] mt-8">
51
- Check out{' '}
52
- <a
53
- href="https://github.com/voidzero-dev/vite-plus"
54
- target="_blank"
55
- rel="noreferrer"
56
- className="font-medium text-[#646cff] hover:text-[#535bf2]"
57
- >
58
- VitePlus
59
- </a>
60
- , the unified toolchain for the web.
61
- </p>
62
-
63
- <p className="text-[#888] mt-4 text-sm">
64
- Click on the VitePlus and React logos to learn more
65
- </p>
66
- </div>
67
- )
68
- }
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -1,9 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import { App } from './App.tsx'
4
-
5
- createRoot(document.getElementById('root')!).render(
6
- <StrictMode>
7
- <App />
8
- </StrictMode>,
9
- )