create-vite-react-boot 1.0.14 → 1.0.16
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/lib/apply.js +169 -17
- package/lib/files.js +0 -11
- package/package.json +1 -1
package/lib/apply.js
CHANGED
|
@@ -24,7 +24,6 @@ export async function applyScaffold({ root /*, template*/ }) {
|
|
|
24
24
|
write(path.join(root, "src", "pages", "Dashboard.tsx"), files.dashboard);
|
|
25
25
|
write(path.join(root, "src", "lib", "storage.ts"), files.storage);
|
|
26
26
|
write(path.join(root, "src", "lib", "auth.tsx"), files.authCtx);
|
|
27
|
-
write(path.join(root, "src", "api", "auth.ts"), files.axiosAuthApi);
|
|
28
27
|
|
|
29
28
|
// 3) 끝 (App.tsx / main.tsx는 이미 cli에서 생성)
|
|
30
29
|
}
|
|
@@ -95,11 +94,175 @@ export default function ProtectedRoute({children}:{children:ReactNode}){
|
|
|
95
94
|
return <>{children}</>
|
|
96
95
|
}
|
|
97
96
|
`,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
home: `import { useState } from "react";
|
|
98
|
+
|
|
99
|
+
function CodeBlock({ children }: { children: string }) {
|
|
100
|
+
const [copied, setCopied] = useState(false);
|
|
101
|
+
return (
|
|
102
|
+
<div className="relative">
|
|
103
|
+
<pre className="mt-2 p-3 bg-gradient-to-br from-blue-50 to-teal-50 border border-pri/30 rounded-lg overflow-x-auto text-sm">
|
|
104
|
+
{children}
|
|
105
|
+
</pre>
|
|
106
|
+
<button
|
|
107
|
+
className="absolute top-2 right-2 rounded-md border border-pri/40 px-2.5 py-1 text-xs text-white bg-pri hover:opacity-90 shadow-sm"
|
|
108
|
+
onClick={async () => {
|
|
109
|
+
await navigator.clipboard.writeText(children);
|
|
110
|
+
setCopied(true);
|
|
111
|
+
setTimeout(() => setCopied(false), 1200);
|
|
112
|
+
}}
|
|
113
|
+
aria-label="copy"
|
|
114
|
+
title="Copy"
|
|
115
|
+
>
|
|
116
|
+
{copied ? "Copied" : "Copy"}
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function Pill({ label, tone = "blue" }: { label: string; tone?: "blue" | "emerald" | "indigo" | "violet" | "cyan" }) {
|
|
123
|
+
const toneMap: Record<string, string> = {
|
|
124
|
+
blue: "bg-blue-50 text-blue-700 border-blue-200",
|
|
125
|
+
emerald: "bg-emerald-50 text-emerald-700 border-emerald-200",
|
|
126
|
+
indigo: "bg-indigo-50 text-indigo-700 border-indigo-200",
|
|
127
|
+
violet: "bg-violet-50 text-violet-700 border-violet-200",
|
|
128
|
+
cyan: "bg-cyan-50 text-cyan-700 border-cyan-200",
|
|
129
|
+
};
|
|
130
|
+
return (
|
|
131
|
+
<span className={\`inline-flex items-center rounded-full px-2.5 py-1 text-xs border \${toneMap[tone]} whitespace-nowrap\`}>
|
|
132
|
+
{label}
|
|
133
|
+
</span>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default function Home() {
|
|
138
|
+
return (
|
|
139
|
+
<section className="space-y-7">
|
|
140
|
+
{/* HERO */}
|
|
141
|
+
<div className="relative overflow-hidden rounded-2xl border border-pri/20">
|
|
142
|
+
<div
|
|
143
|
+
className="absolute inset-0"
|
|
144
|
+
style={{
|
|
145
|
+
background:
|
|
146
|
+
"radial-gradient(700px 300px at -10% 0%, rgba(37,99,235,0.22), transparent 60%), radial-gradient(700px 300px at 110% 10%, rgba(16,185,129,0.20), transparent 60%), linear-gradient(180deg, rgba(255,255,255,0.9), rgba(255,255,255,0.85))",
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
<div className="relative p-6 md:p-9">
|
|
150
|
+
<div className="flex flex-wrap gap-2">
|
|
151
|
+
<Pill label="Vite" tone="blue" />
|
|
152
|
+
<Pill label="React 18" tone="indigo" />
|
|
153
|
+
<Pill label="TypeScript" tone="violet" />
|
|
154
|
+
<Pill label="Tailwind" tone="emerald" />
|
|
155
|
+
<Pill label="React Router" tone="cyan" />
|
|
156
|
+
</div>
|
|
157
|
+
<h1 className="mt-4 text-2xl md:text-3xl font-semibold tracking-tight text-pri">
|
|
158
|
+
create-vite-react-boot
|
|
159
|
+
</h1>
|
|
160
|
+
<p className="text-muted mt-2 max-w-2xl">
|
|
161
|
+
Vite + React + TypeScript + Tailwind 기반 스타터입니다. 로그인/회원가입은
|
|
162
|
+
<strong> localStorage</strong>만 사용하며 서버는 없습니다. 생성·설치·스캐폴딩·개발 서버 실행까지 한 번에 처리합니다.
|
|
163
|
+
</p>
|
|
164
|
+
<div className="mt-5 flex gap-10 text-sm">
|
|
165
|
+
<a href="/login" className="btn btn-primary">로그인</a>
|
|
166
|
+
<a href="/about" className="btn">About</a>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<div className="h-1 bg-gradient-to-r from-blue-500 via-cyan-400 to-emerald-500" />
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{/* 사용자 안내 */}
|
|
173
|
+
<div className="card border-l-4 border-l-emerald-400">
|
|
174
|
+
<h2 className="text-lg font-semibold text-emerald-700">사용자 안내</h2>
|
|
175
|
+
<ul className="list-disc pl-5 mt-2 space-y-1 text-sm">
|
|
176
|
+
<li>계정 정보는 <code>localStorage</code>에 저장됩니다 (예: <code>session_v1</code>).</li>
|
|
177
|
+
<li>암호화/서버 검증이 없으므로 민감한 정보는 입력하지 마세요.</li>
|
|
178
|
+
<li>같은 브라우저에서만 유지됩니다. 시크릿 모드/브라우저 변경 시 사라질 수 있습니다.</li>
|
|
179
|
+
<li>초기화: 개발자 도구 → Application/저장소 → <code>localStorage</code> 키 삭제.</li>
|
|
180
|
+
</ul>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{/* 설치 & 실행 */}
|
|
184
|
+
<div className="card border-l-4 border-l-blue-500">
|
|
185
|
+
<h2 className="text-lg font-semibold text-blue-700">설치 & 실행</h2>
|
|
186
|
+
|
|
187
|
+
<div className="mt-3">
|
|
188
|
+
<div className="text-sm font-medium text-ink">1) 프로젝트 생성 (자동 설치/실행)</div>
|
|
189
|
+
<CodeBlock>{\`# npm
|
|
190
|
+
npm create vite-react-boot@latest
|
|
191
|
+
|
|
192
|
+
# pnpm
|
|
193
|
+
pnpm dlx create-vite-react-boot@latest
|
|
194
|
+
|
|
195
|
+
# yarn
|
|
196
|
+
yarn create vite-react-boot\`}</CodeBlock>
|
|
197
|
+
<p className="text-xs text-muted mt-2">
|
|
198
|
+
프로젝트 이름 입력 후 설치/스캐폴딩이 완료되면 개발 서버가 자동 시작됩니다.
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div className="mt-4">
|
|
203
|
+
<div className="text-sm font-medium text-ink">2) 자동 실행이 안 된 경우</div>
|
|
204
|
+
<CodeBlock>{\`cd <프로젝트명>
|
|
205
|
+
npm run dev # 또는 pnpm dev / yarn dev\`}</CodeBlock>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div className="mt-4">
|
|
209
|
+
<div className="text-sm font-medium text-ink">3) 수동 설치가 필요한 경우</div>
|
|
210
|
+
<CodeBlock>{\`cd <프로젝트명>
|
|
211
|
+
npm install # 또는 pnpm install / yarn install
|
|
212
|
+
npm run dev\`}</CodeBlock>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<div className="mt-4">
|
|
216
|
+
<div className="text-sm font-medium text-ink">4) 빌드 & 프리뷰</div>
|
|
217
|
+
<CodeBlock>{\`npm run build
|
|
218
|
+
npm run preview\`}</CodeBlock>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
{/* 요약 */}
|
|
223
|
+
<div className="grid md:grid-cols-3 gap-4">
|
|
224
|
+
<div className="card border-t-2 border-t-blue-400">
|
|
225
|
+
<h3 className="font-medium text-blue-700">기능 요약</h3>
|
|
226
|
+
<ul className="list-disc pl-5 mt-2 space-y-1 text-sm">
|
|
227
|
+
<li>생성 → 설치(조용히) → 스캐폴딩 → 자동 실행</li>
|
|
228
|
+
<li>홈/로그인/About 기본 라우트</li>
|
|
229
|
+
<li>localStorage 세션 유지</li>
|
|
230
|
+
<li>기본 UI 유틸 클래스(.card, .btn, .input)</li>
|
|
231
|
+
</ul>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div className="card border-t-2 border-t-violet-400">
|
|
235
|
+
<h3 className="font-medium text-violet-700">라우트</h3>
|
|
236
|
+
<ul className="list-disc pl-5 mt-2 space-y-1 text-sm">
|
|
237
|
+
<li><code>/</code> : 홈</li>
|
|
238
|
+
<li><code>/login</code> : 로그인</li>
|
|
239
|
+
<li><code>/about</code> : 소개</li>
|
|
240
|
+
</ul>
|
|
241
|
+
<p className="text-xs text-muted mt-2">
|
|
242
|
+
로그인 실패 시 서버 메시지는 숨기고 기본 안내 문구만 표시합니다.
|
|
243
|
+
</p>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<div className="card border-t-2 border-t-emerald-400">
|
|
247
|
+
<h3 className="font-medium text-emerald-700">스크립트 & 스택</h3>
|
|
248
|
+
<div className="mt-2 text-sm">
|
|
249
|
+
<div className="mb-2">
|
|
250
|
+
<div className="text-muted">scripts</div>
|
|
251
|
+
<pre className="mt-1 p-2 bg-emerald-50/60 border border-emerald-200 rounded text-xs overflow-x-auto">{\`"dev": "vite",
|
|
252
|
+
"build": "tsc -b && vite build",
|
|
253
|
+
"preview": "vite preview"\`}</pre>
|
|
254
|
+
</div>
|
|
255
|
+
<div>
|
|
256
|
+
<div className="text-muted">stack</div>
|
|
257
|
+
<p className="mt-1 text-sm text-muted">
|
|
258
|
+
Vite · React 18 · TypeScript · Tailwind · React Router
|
|
259
|
+
</p>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</section>
|
|
265
|
+
);
|
|
103
266
|
}
|
|
104
267
|
`,
|
|
105
268
|
login: `import { FormEvent, useState } from 'react'
|
|
@@ -285,17 +448,6 @@ export function useAuth() {
|
|
|
285
448
|
if (!ctx) throw new Error('useAuth must be used within AuthProvider')
|
|
286
449
|
return ctx
|
|
287
450
|
}
|
|
288
|
-
`,
|
|
289
|
-
axiosAuthApi: `import { api } from './client'
|
|
290
|
-
type Session = { id:string; email:string; name:string }
|
|
291
|
-
export async function register(email:string, name:string, password:string):Promise<Session>{
|
|
292
|
-
const { data } = await api.post('/register', { email, name, password })
|
|
293
|
-
return data
|
|
294
|
-
}
|
|
295
|
-
export async function login(email:string, password:string):Promise<Session>{
|
|
296
|
-
const { data } = await api.post('/login', { email, password })
|
|
297
|
-
return data
|
|
298
|
-
}
|
|
299
451
|
`,
|
|
300
452
|
};
|
|
301
453
|
}
|
package/lib/files.js
CHANGED
|
@@ -258,17 +258,6 @@ api.interceptors.request.use(cfg=>{
|
|
|
258
258
|
}
|
|
259
259
|
return cfg
|
|
260
260
|
})
|
|
261
|
-
`,
|
|
262
|
-
axiosAuthApi: `import { api } from './client'
|
|
263
|
-
type Session = { id:string; email:string; name:string }
|
|
264
|
-
export async function register(email:string, name:string, password:string):Promise<Session>{
|
|
265
|
-
const { data } = await api.post('/register', { email, name, password })
|
|
266
|
-
return data
|
|
267
|
-
}
|
|
268
|
-
export async function login(email:string, password:string):Promise<Session>{
|
|
269
|
-
const { data } = await api.post('/login', { email, password })
|
|
270
|
-
return data
|
|
271
|
-
}
|
|
272
261
|
`,
|
|
273
262
|
mainTsx: `import React from 'react'
|
|
274
263
|
import ReactDOM from 'react-dom/client'
|